티스토리 뷰


fun main(args: Array<String> {
    println("Hello, World")
}
  • 함수를 선언할 때 fun 키워드를 사용한다.
  • 파라미터 이름 뒤에 그 파라미터의 타입을 작성한다. 이는 변수를 선언할 때도 마찬가지로 방식으로 타입을 지정한다.
  • 함수를 최상위 수준에 정의할 수 있다. 즉, 클래스 안에 함수를 넣어야할 필요가 없다.
  • 코틀린에서는 자바와 달리 배열 처리를 위한 문법이 따로 존재하지 않는다. 배열도 일반적인 클래스와 마찬가지이다.
  • 코틀린 표준 라이브러리는 여러 가지 표준 자바 라이브러리 함수를 간결하게 사용할 수 있게 감싼 래퍼(wrapper)를 제공한다.
  • 세미콜론(;)을 붙이지 않아도 된다.

함수

함수 선언은 fun 키워드로 시작하고, fun 다음에는 함수 이름이 온다. 아래 max라는 이름을 가진 함수의 이름 뒤에는 괄호 안에 파라미터 목록이 위치하고, 함수의 반환 타입은 파라미터 목록의 닫는 괄호 다음에 오는데, 괄호와 반환 타입 사이를 콜론(:)으로 구분해야 한다.

fun max(a: Int, b: Int): Int {
    return if(a > b) a else b
}

문(statement)과 식(expression)의 구분

코틀린에서 if는 문이 아니라 식이다. 식은 값을 만들어 내며 다른 식의 하위 요소로 계산에 참여할 수 있는 반면 문은 자신을 둘러싸고 있는 가장 안쪽 블록의 최상위 요소로 존재하며 아무런 값을 만들어내지 않는다는 차이가 있다. 자바에서는 모든 제어 구조가 문인 반면 코틀린에서는 루프를 제외한 대부분의 제어 구조가 식이다.


식이 본문인 함수

아래와 같이 중괄호를 없애고 return을 제거하면서 등호(=)를 식 앞에 붙이면 더 간결하게 함수를 표현할 수 있다.

fun max(a: Int, b: Int): Int = if (a > b) a else b

본문이 중괄호로 둘러싸인 함수를 블록이 본문인 함수라 부르고, 등호와 식으로 이뤄진 함수를 식이 본문인 함수라고 부른다.


식이 본문인 함수의 경우, 굳이 사용자가 반환 타입을 적지 않더라도 컴파일러가 함수 본문 식을 분석해서 식의 결과 타입을 함수 반환 타입으로 정해주는데, 이렇게 컴파일러가 타입을 분석하는 것을 타입 추론이라고 한다.


변수

자바에서는 변수를 선언할 때 타입이 맨 앞에 오지만, 코틀린에서는 키워드로 변수 선언을 시작하는 대신 변수 이름 뒤에 타입을 명시하거나 생략하도록 허용한다.

val question = "삶, 우주, 그리고 모든 것에 대한 궁극적인 질문"
val answer = 42
val number: Int = 52

변경 가능한 변수와 변경 불가능한 변수

변수 선언 시 사용하는 키워드는 아래와 같이 2가지로 구분된다.

  • val(값을 뜻하는 value에서 따옴) - 변경 불가능한 참조를 저장하는 변수다. val로 선언된 변수는 일단 초기화되고 나면 재대입이 불가능하다. 자바의 final 변수에 해당한다.
  • var(변수를 뜻하는 variable에서 따옴) - 변경 가능한 참조다. 자바의 일반 변수와 마찬가지로 이런 변수의 값은 바뀔 수 있다.

기본적으로 모든 변수를 val 키워드를 사용해 불변 변수로 선언하고, 나중에 꼭 필요할 때만 var로 변경하도록 하자.


val 참조 자체는 불편일지라도 그 참조가 가리키는 객체의 내부 값은 변경될 수 있다.

val languages = arrayListOf("Java")
languages.add("Kotlin")

프로퍼티

자바에서는 필드와 접근자를 묶어 프로퍼티라고 부르며, 프로퍼티라는 개념을 활용하는 프레임워크가 많다. 코틀린은 프로퍼티를 언어 기본 기능으로 제공하며, 코틀린 프로퍼티는 자바의 필드와 접근자 메서드를 완전히 대신한다. 클래스에서 프로퍼티를 선언할 때는 변수를 선언하는 방법과 마찬가지로 val이나 var를 사용한다. val로 선언한 프로퍼티는 읽기 전용이며, var로 선언한 프로퍼티는 변경 가능하다.

class Person(
    val name: String, // 읽기 전용 프로퍼티로, 코틀린은 (비공개)필드와 필드를 읽는 단순한 (공개) 게터를 만들어 낸다.
    val isMarried: Boolean // 쓸 수 있는 프로퍼티로, 코틀린은 (비공개)필드, (공개)게터, (공개)세터를 만들어 낸다.
)

기본적으로 코틀린에서 프로퍼티를 선언하는 방식은 프로퍼티와 관련 있는 접근자를 선언하는 것이다. 코틀린은 값을 저장하기 위한 비공개 필드와 그 필드에 값을 저장하기 위한 세터, 필드의 값을 읽기 위한 게터로 이뤄진 간단한 디폴트 접근자 구현을 제공한다. 대부분의 프로퍼티에는 그 프로퍼티의 값을 저장하기 위한 필드가 있는데, 이를 뒷받침하는 필드(backing field)라고 한다.


커스텀 접근자

직사각형 클래스인 Rectangle은 자신이 정사각형인지 알려주는 기능이 필요하다. 커스텀 접근자를 이용하면, 이 기능을 제공하기 위해 별도의 필드를 만들어 저장할 필요 없이 사각형의 너비와 높이가 같은지 검사하여 정사각형 여부를 그때그때 알 수 있다.

class Rectangle(val height: Int, val width: Int) {
    val isSquare: Boolean
        get() {
            return height == width
        }
}

위 게터는 클라이언트가 프로퍼티에 접근할 때마다 프로퍼티 값을 매번 다시 계산한다.


enum과 when

when은 자바의 switch를 대치하되 훨씬 더 강력한 기능을 제공한다. when에 대해 알아보며 코틀린에서 enum을 선언화는 방법과 스마트 캐스트(smart cast)에 대해서도 살펴보자.


enum 클래스 정의

자바에서는 enum을 사용하지만, 코틀린에서는 enum class를 사용한다.

enum class Color(
    val r: Int, val g: Int, val b: Int
) {
    RED(255, 0, 0), ORANGE(255, 165, 0),
    YELLOW(255, 255, 0), GREEN(0, 255, 0), BLUE(0, 0, 255),
    INDIGO(75, 0, 130), VIOLET(238, 130, 238);

    fun rgb() = (r * 256 + g) * 256 + b
}

enum에서도 일반적인 클래스와 마찬가지로 생성자와 프로퍼티를 선언하고, 각 enum 상수를 정의할 때는 그 상수에 해당하는 프로퍼티 값을 지정해야만 한다. 또한, enum 클래스 안에 메서드를 정의하는 경우 반드시 enum 상수 목록과 메서드 정의 사이에 세미콜론을 넣어야 한다.


when으로 enum 클래스 다루기

코틀린에서 whenif와 마찬가지로 값을 만들어내는 식이다. 따라서 식이 본문인 함수에 when을 바로 사용할 수 있다.

fun getMnemonic(color: Color) = 
    when (color) {
        Color.RED -> "Richard"
        Color.ORANGE -> "Of"
        Color.YELLOW -> "York"
        Color.GREEN -> "Gave"
        Color.BLUE -> "Battle"
        Color.INDIGO -> "In"
        Color.VIOLET -> "Vain"
    }

위의 코드는 color로 전달된 값과 같은 분기를 찾는다. 자바와 달리 각 분기의 끝에 break를 넣지 않아도 된다. 만약 한 분기 안에서 여러 값을 매치 패턴으로 사용해야 한다면, 값 사이를 콤마(,)로 분리하면 된다.

fun getWarmth(color: Color) = when (color) {
        Color.RED, Color.ORANGE, Color.YELLOW -> "warm"
        Color.GREEN -> "neutral
        Color.BLUE, Color.INDIGO, Color.VIOLET -> "cold"
    }

when과 임의의 객체를 함께 사용

분기 조건에 상수만을 사용할 수 있는 자바 switch와 달리 코틀린 when의 분기 조건은 임의의 객체를 허용한다.

fun mix(c1: Color, c2: Color) = 
    when (setOf(c1, c2)) {
        setOf(RED, YELLOW) -> ORANGE
        setOf(YELLOW, BLUE) -> GREEN
        setOf(BLUE, VIOLET) -> INDIGO
        else -> throw Exception("Dirty color")
    }

인자 없는 when 사용

위의 코드는 호출될 때마다 함수 인자로 주어진 두 색이 when의 분기 조건에 있는 다른 두 색과 같은지 비교하기 위해 여러 Set 인스턴스를 생성한다. 보통은 이런 비효율성이 크게 문제가 되지 않지만, 이 함수가 자주 호출된다면 불필요한 가비지 객체가 늘어나는 것을 방지하기 위해 함수를 고쳐 사용하는 것이 낫다. 인자가 없는 when 식을 시용하면 불필요한 객체 생성을 막을 수 있다.

fun mixOptimized(c1: Color, c2: Color) = 
    when {
        (c1 == RED && c2 == YELLOW) 
        || (c1 == YELLOW && c2 == RED) 
        -> ORANGE

        (c1 == YELLOW && c2 == BLUE)
        || (c1 == BLUE && c2 == YELLOW)
        -> GEEEN

        (c1 == BLUE && c2 == VIOLET)
        || (c1 == VIOLET && c2 == BLUE)
        -> INDIGO

        else -> throw Exception("Dirty color")
    }

when에 아무 인자도 없으려면 각 분기의 조건이 불리언 결과를 계산하는 식이어야 한다.


스마트 캐스트

코틀린에서는 is를 사용해 변수 타입을 검사한다. is 검사는 자바의 instanceof와 비슷하다. 하지만 자바에서 어떤 변수의 타입을 instanceof로 확인한 다음에 그 타입에 속한 멤버에 접근하기 위해서는 명시적으로 변수 타입을 캐스팅해야 한다. 코틀린에서는 프로그래머 대신 컴파일러가 캐스팅을 해준다. 어떤 변수가 원하는 타입인지 일단 is로 검사하고 나면 굳이 변수를 원하는 타입으로 캐스팅하지 않아도 마치 처음부터 그 변수가 원하는 타입으로 선언된 것처럼 사용할 수 있다. 이렇듯 컴파일러가 캐스팅해주는 것을 스마트 캐스트(smart cast)라고 한다.


if와 when의 분기에서 블록 사용

ifwhen 모두 분기에 블록을 사용할 수 있다. 이런 경우 블록의 마지막 문장이 블록 전체의 결과가 된다.

fun evalWithLogging(e: Expr): Int =
    when (e) {
        is Num -> {
            println("num: ${e.value}")
            e.value
        }
        is Sum -> {
            val left = evalWithLogging(e.left)
            val right = evalWithLogging(e.right)
            println("sum: $left + $right")
            left + right
        }
        else -> throw IllegalArgumentException("Unknown expression")
    }

'블록의 마지막 식이 블록의 결과'라는 규칙은 블록이 값을 만들어내야 하는 경우 항상 성립한다.


for 루프

코틀린의 forC#과 마찬가지로 for <아이템> in <원소들> 형태를 취한다. 이런 for 루프는 자바에서와 마찬가지로 컬렉션에 대한 이터레이션에 가장 많이 쓰인다.


수에 대한 이터레이션: 범위와 수열

코틀린에는 자바의 for 루프에 해당하는 요소가 없다. 이런 루프의 가장 흔한 용례인 초깃값, 증가 값, 최종 값을 사용한 루프를 대신하기 위해 코틀린에서는 범위를 사용한다.


범위는 기본적으로 두 값으로 이뤄진 구간이다. 보통은 그 두 값은 정수 등의 숫자 타입의 값이며 .. 연산자로 시작 값과 끝 값을 연결해서 범위를 만든다.

val onToTen = 1..10

코틀린의 범위는 폐구간(닫힌 구간) 또는 양끝을 포함하는 구간이다. 이는 두 번째 값(위에서는 10)이 항상 범위에 포함된다는 뜻이다. 정수 범위로 수행할 수 있는 가장 단순한 작업은 범위에 속한 모든 값에 대한 이터레이션이다. 이런 식으로 어떤 범위에 속한 값을 일정한 순서로 이터레이션하는 경우를 수열(progression)이라고 부른다.


in으로 컬렉션이나 범위의 원소 검사

in 연산자를 사용해 어떤 값이 범위에 속하는지 검사할 수 있다. 반대로 !in를 사용하면 어떤 값이 범위에 속하지 않는지 검사할 수 있다.

fun isLeffter(c: Char) = c in 'a'..'z' || c in 'A'..'Z'
fun isNotDigit(c: Char) = c !in '0'..'9'

println(isLetter('q')) // true
println(isNotDigit('x')) // true

위와 같은 비교 로직은 표준 라이브러리의 범위 클래스 구현 안에 깔끔하게 감춰져 있다.

c in 'a'..'z' -> 'a' <= c && c <= 'z'로 변환된다.

예외 처리

코틀린의 예외 처리는 자바나 다른 언어의 예외 처리와 비슷하다. 함수는 정상적으로 종료할 수 있지만 오류가 발생하면 예외를 던질 수 있다. 함수를 호출하는 쪽에서는 그 예외를 잡아 처리할 수 있다. 발생한 예외를 함수 호출 단에서 처리하지 않으면 함수 호출 스택을 거슬러 올라가면서 예외를 처리하는 부분이 나올 때까지 예외를 다시 던진다.

if (percentage !in 0..100) {
    throw IllegalArgumentException(
        "A percentage value must be between 0 and 100: $percentage")
}

다른 클래스와 마찬기자로 예외 인스턴스를 만들 때도 new를 붙일 필요가 없고, 자바와 달리 코틀린의 throw는 식이므로 다른 식에 포함될 수 있다.

val percentage = 
    if (number in 0..100)
        number
    else
        throw IllegalArgumentException("A percentage value must be between 0 and 100: $percentage")

위 예제에서는 if의 조건이 참이므로 프로그램이 정상 동작해서 percentage 변수가 number의 값으로 초기화 되지만, 만약 조건이 거짓이면 변수가 초기화되지 않는다.



요약

  • 함수를 정의할 때 fun 키워드를 사용한다. valvar는 각각 읽기 전용 변수와 변경 가능한 변수를 선언할 때 쓰인다.
  • 다른 언어에도 있는 if는 코틀린에서 식이며, 값을 만들어 낸다.
  • 코틀린 when은 자바의 switch와 비슷하지만 더 강력하다.
  • 어떤 변수의 타입을 검사하고 나면 굳이 그 변수를 캐스팅하지 않아도 검사한 타입의 변수처럼 사용할 수 있다. 그런 경우 컴파일러가 스마트 캐스르를 활용해 자동으로 타입으로 바꿔준다.
  • for, while, do-while루프는 자바가 제공하는 같은 키워드의 기능과 비슷하다. 하지만 코틀린의for는 자바의for`보다 더 편리하다.
  • 1..5와 같은 식은 범위를 만들어낸다. 범위와 수열은 코틀린에서 같은 문법을 사용하며, for 루프에 대해 같은 추상화를 제공한다. 어떤 값이 범위 안에 들어 있거나 들어있지 않은지 검사하기 위해서 in이나 !in을 사용한다.
  • 코틀린 예외 처리는 자바와 비슷하다. 다만 코틀린에서는 함수가 던질 수 있는 예외를 선언하지 않아도 된다.


Reference
디미트리 제메로프, 스베트라나 이사코바. 『Kotlin In Action』. 에이콘, 2017.

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/01   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함