-
2장 - 코틀린 기초Kotlin/코틀린인액션요약 2022. 9. 10. 00:01728x90
2장에서 다루는 내용
- 함수, 변수 , 클래스 enum 프로퍼티를 선언하는 방법
- 코틀린의 여러 제어구조
- 스마트 캐스트
- 예외 던지기와 예외 잡기
단, 여기에서 배운 내용으로 작성하는 코드는 코틀린다운 코드라고 부르기는 어려움
함수와 변수
fun main(args: Array<String>){ println("Hello,world!") }
함수를 선언할 때 fun 키워드를 사용합니다.
파라미터 이름 뒤에 그 파라미터의 타입을 씁니다.
System.out.println 대신에 println을 사용합니다.(자바 라이브러리 함수를 간결하게 사용할 수 있는 래퍼를 제공합니다)
끝에 세미콜론(;)을 사용하지 않아도 됩니다.
함수
fun max(a: Int, b:Int) : Int{ return if(a>b) a else b } println(max(1,2)) //출력 2
fun으로 함수를 선언하고 함수 이름은 max, 파라미터 목록은 a, b 반환 타입은 Int입니다.
위처럼 본문이 중괄호로 둘러쌓인 함수를 블록이 본문인 함수라고 합니다.
반대로 다음과 등호와 식으로 이뤄진 함수를 식이 본문인 함수라고 부릅니다.
fun max(a: Int, b: Int) Int = if(a>b) a else b //식이 본문인 함수
코틀린에서는 식이 본문인 함수가 더 자주 쓰이며 여기에 if, when, try 등의 더 복잡한 식도 자주 쓰입니다.
또한 여기서 반환타입인 Int를 생략해도 컴파일러가 함수 본문 식을 분석하여 결과 타입을 추론합니다. (타입 추론)
변수
val question = "삶, 우주, 그리고 모든 것에 대한 궁극적인 질문" val answer = 42 val answer : Int = 42
타입 추론을 사용해도 되고 타입을 명시해도 됩니다.
부동소수점을 사용하면 변수 타입은 Double이 됩니다.
val immutable = 42; var mutable = 42;
val은 value의 약자로 변경 불가능한 참조를 저장하는 변수입니다. (자바의 final)
var은 variable의 약자로 변경 가능한 참조를 저장하는 변수입니다.
var키워드를 사용하면 변수의 값은 변경할 수 있지만 변수의 타입은 바꿀 수 없습니다.
만약 다른 타입의 값을 저장하고 싶다면 변환 함수를 사용하여 타입을 변환시키거나 강제 형 변환을 해야 합니다.
문자열 템플릿
fun main(args: Array<String>){ val name = if(args.size >0) args[0] else "Kotlin" println("Hello, $name!") }
변수 앞에 $를 사용하여 변수를 문자열 안에서 사용할 수 있습니다.
변수 이름뿐만 아니라 복잡한 식도 {}로 감싸서 넣을 수 있습니다.
변수 이름을 사용할 때도 {}로 감싸는 습관을 들이면 더 좋습니다.
클래스와 프로퍼티
간단한 자바 클래스
public clas Person{ private final String name; public Person(String name){ this.name = name; } public String getName(){ return name; } }
이를 코틀린으로 변환하면 다음과 같습니다. (코틀린 파일에 자바 코드를 복사하면 변환할 거냐는 메시지가 나옴)
class Person(val name: String)
프로퍼티
자바에서는 필드와 접근자를 한데 묶어 프로퍼티라고 부릅니다.
코틀린은 프로퍼티를 언어 기본 기능으로 제공합니다.
앞서 변수에서 보았듯이 val은 읽기 전용이며 var는 읽기/쓰기가 가능합니다.
코틀린은 val을 사용하면 비공개(private) 필드를 만들며 게터를 만들어냅니다.
코틀린은 var를 사용하면 비공개(private) 필드를 만들며 게터, 세터를 만들어냅니다.
코틀린 클래스 사용하기
val person = Person("Bob", true) println(person.name) //Bob println(person.isMarried) //true
프로퍼티 이름으로 사용해도 코틀린이 자동으로 게터를 호출해줍니다.
마찬가지로 person.isMarreid = false는 세터처럼 동작합니다.
프로퍼티 접근자 커스텀하기
직사각형 클래스인 Rectangle을 정의하며 자신이 정사각형인지 알려주는 기능을 만들어보겠습니다.
class Rectangle(val height: Int, val width: Int){ val isSqure: Boolean get(){ return height== width } } val rectangle = Rectangle(41,43) println(rectangle.isSqure) //false
디렉터리와 패키지
패키지를 import 하면 패키지 안에 있는 모든 선언을 사용할 수 있습니다.
또한 이름으로 함수를 임포트 하여 사용할 수 있습니다.
enum과 when
when은 자바의 switch를 대치하면 훨씬 더 강력합니다.
앞으로 더 자주 사용할 프로그래밍 요소입니다.
enum은 자바보다 코틀린에서 더 많은 키워드를 사용해야 하는 흔치 않은 예입니다.
enum class Color{ RED, ORANGE, YELLOW, GREEN }
자바와 마찬가지로 enum 클래스에 프로퍼티나 메서드를 정의할 수 있습니다.
enum class Color( var r: Int, var g: Int, var b: Int ){ RED(255,0,0), GREEN(0,255,0), BLUE(0,0,255); fun rgb() = (r * 256 + g) * 256 + b } println(Color.BLUE.rgb()) //255 출력
코틀린에서 유일하게 세미콜론이 필수인 부분입니다.
when을 활용한 enum
fun getMnemonic(color: Color) = when (color){ Color.RED -> "Richard" COLOR.GREEN -> "Gave" Color.BLUE -> "Battle" }
각 분기 끝에 break를 넣지 않아도 됩니다.
또한 , 을 활용하여 한 분기에 여러 값을 사용할 수 있습니다.
Color.RED, Color.ORANGE, Color.YELLOW -> "warm"
enum 상수 값을 임포트 하여 enum 클래스 수식어 없이 enum을 사용할 수 있습니다.
import ch02.colors.Color.* fun getWarmth(color: Color) = when(color){ RED, ORANGE, YELLO -> "warm" }
인자 없는 when
매번 인자를 사용하여 분기 조건에 있는 것을 비교해야 합니다.
인자가 없는 when을 사용하면 불필요한 객체 생성을 막을 수 있지만 코드 읽기가 약간 어려워집니다.
fun mixOptimized(c1: Color, c2: Color) = when{ (c1 == YELLO && c2 == BLUE) || (c1 == BLUE && c2 == YELLO) -> GREEN } println(mixOptimized(BLUE,YELLO)) //출력 : GREEN
스마트 캐스트
interface Expr class Num(val value: Int) : Expr class Sum(val left:Expr, val right: Expr) : Expr
Expr은 인터페이스입니다.
Num 클래스는 value라는 프로퍼티만 존재하는 단순한 클래스로 Expr 인터페이스를 구현합니다.
Sum 클래스는 Expr 타입의 객체라면 어떤 것이나 Sum 연산의 인자가 될 수 있습니다.
따라서 Num, Sum 클래스가 인자로 올 수 있습니다.
인터페이스를 이용한 연산을 구현한 코드입니다.
interface Expr class Num(val value: Int) : Expr class Sum(val left: Expr, val right: Expr) : Expr fun main() { println(eval(Sum(Num(1), Num(2)))) } fun eval(e: Expr) : Int{ if( e is Num){ val n = e as Num return n.value } if( e is Sum){ return eval(e.right) + eval(e.left) } throw IllegalArgumentException("Unknown expression") }
코틀린은 is로 변수 타입을 검사합니다.
이때 e as Num으로 캐스팅하는 게 불필요합니다.
코틀린은 is로 검사하고 나면 캐스팅하지 않아도 처음부터 그 변수가 원하는 타입으로 선언된 것처럼 사용할 수 있습니다.
이를 스마트 캐스트라고 부릅니다.
리팩터링 : if를 when으로 변경
코틀린에서 if는 값을 만들어 내기 때문에 자바와 달리 3항 연산자가 따로 없습니다.
이런 특성을 이용하면 if 식을 본문으로 사용하여 return 문과 중괄호를 없앨 수 있습니다.
fun eval(e: Expr) : Int = if( e is Num){ e.value } else if(e is Sum){ eval(e.right) + eval(e.left) } else throw IllegalArgumentException("Unknown expression") |
이 코드를 when으로 변환하면 더 다듬을 수 있습니다.
fun eval(e: Expr) : Int = when (e) { is Num -> e.value is Sum -> eval(e.right) + eval(e.left) else -> throw IllegalArgumentException("Unknown expression") }
while과 for 사용
while, do-while이 존재하며 자바와 크게 다르지 않습니다.
코틀린에서 자바의 for 루프에 해당하는 요소가 없습니다.
.. 연산자로 시작 값과 끝 값을 연결해서 범위를 만들어냅니다.
val oneToTen = 1.. 10
코틀린의 범위는 폐구간 즉, 양끝을 포함하는 구간입니다. (즉, 1.. 10은 10을 포함합니다)
피즈 버즈 게임을 구현해보겠습니다.
피즈버즈 게임은 3으로 나누어 떨어지면 피즈, 5로 나누어 떨어지면 버즈 , 3과 5로 모두 나누어 떨어지면 피즈 버즈를 외치는 게임입니다.
fun fizzBuzz(i: Int) = when{ i % 15 == 0 -> "FizzBuzz " i % 3 == 0 -> "Fizz " i % 5 == 0 -> "Buzz " else -> "$i" //다른 경우 자기자신 반환 } fun main() { for(i in 1..100){ print(fizzBuzz(i)) } }
이번에는 100부터 거꾸로 세되 짝수만으로 게임을 진행하는 코드입니다.
fun main() { for(i in 100 downTo 1 step 2){ print(fizzBuzz(i)) } }
만약 1.. size을 활용하여 1~size-1까지 표현하고 싶다면 for(x in 0 until size)를 활용할 수 있습니다.
Map에 대한 이터레이션
import java.util.* fun main() { val binaryReps = TreeMap<Char, String>() for( c in 'A'..'F') { val binary = Integer.toBinaryString(c.toInt()) binaryReps[c] = binary } for((letter, binary) in binaryReps){ println("$letter = $binary") } }
in 키워드를 통해 컬렉션이나 범위의 원소 검사
fun isLetter(c: Char) = c in 'a'..'z' || c in 'A'..'Z' fun isNotDigit(c: Char) = c !in '0'..'9'
when과 in 같이 활용하기
fun recognize(c: Char) = when(c){ in '0'..'9' -> "It's a digit" in 'a'..'z', in 'A'..'Z' -> "It's a letter" else -> "I don't know.." }
코틀린의 예외 처리
코틀린의 예외처리는 자바나 다른 언어의 예외 처리와 비슷합니다.
다른 점은 예외 인스턴스를 만들 때도 new를 붙일 필요가 없습니다.
try, catch, finally
import java.io.BufferedReader fun main() { fun readNumber(reader: BufferedReader) : Int? { //던질 수 있는 예외를 명시할 필요가 없습니다 try{ val line = reader.readLine() return Integer.parseInt(line) } catch(e: NumberFormatException){ //예외 타입을 :의 오른쪽에 씁니다 return null } finally { reader.close() } } }
try 식으로 사용하기
fun readNumber(reader : BufferedReader) { val number = try{ Integer.parseInt(reader.readLine()) } catch (e: NumberFormatException){ null } println(number) } fun main() { val reader= BufferedReader(StringReader("not a number")) readNumber(reader) }
728x90'Kotlin > 코틀린인액션요약' 카테고리의 다른 글
6장 - 코틀린 타입 시스템 (0) 2022.09.16 5장 - 람다로 프로그래밍 (0) 2022.09.13 4장 - 클래스, 객체, 인터페이스 (1) 2022.09.12 3장 - 함수 정의와 호출 (1) 2022.09.11 1장 - 코틀린소개 (0) 2022.09.08