최근 코틀린을 다룰 일이 없다가 스프링이란 것을 공부해보면서 뭐가 변했는지 알고가면 좋을 것 같아 코틀린 독스를 펼쳤다.

Docs - Whats new in Kotlin 2.0.0를 바탕으로 요약을 해보는 글을 적어보려한다.

솔직히 내용중에 알빠노싶은것도 많은데(어차피 쓰다보면 굳이 이것까지 알 필요없고 자연스레 이제 이런것도 되네 싶은 것들이나 사소한 개선들), 그래도 새 출발하는 마음으로 정리해보자.

결론부터 말하자면 그냥 코틀린을 쓰던 사람들이 특별히 따로 배워야할 문법은 없다고 봐도 무방하다.

주로 JVM과 K2에 대해서만 좀 다루었으므로 자세한 모든 사항은 위 링크를 참조하자.

image.png


Kotlin / JVM

Kotlin 2부터 Java 22를 타겟으로 하는 .class파일을 생성할 수 있게 되었다.

Generation of lambda functions using invokedynamic

invokedynamic이란 친구를 통해 람다 함수를 생성해내는 방식을 default로 변경했다고 한다.

이는 이전의 익명 클래스를 생성하는 방식보다 바이너리 크기를 감소시키며 효율적이라고 한다.

invokedynamic은 JVM optimizations를 통해 추후 JVM의 개선으로 더 성능적으로 개선될 여지가 있는 방식이라고 한다.

현재는 세 가지 한계가 있다.

  1. 이것으로 만들어진 람다는 직렬화되지 못한다.
  2. 현재 실험적 API인 reflect()는 이것으로 만들어진 람다를 지원하지 않는다. (이 함수는 함수의 런타임 reflection을 도와주는 함수임)
  3. 람다에 대한 .toString()의 가독성이 나쁠 수 있다.

특정 람다에 대해서 이전 구현을 유지하려면 @JvmSerializableLambda를 람다에 붙이거나 컴파일러 옵션에 -Xlambdas=class를 선언하여 전체 프로젝트에서 invokedynamic을 비활성화시킬 수 있다고 한다.

The kotlinx-metadata-jvm-library is Stable

kotlin-metadata-jvm 란 이름으로 kotlinx -> kotlin으로 변경되었으며 이제부터 Kotlin의 release cycle에 따른다고 한다.

이는 Kotlin/JVM 컴파일러로 생성된 바이너리 파일의 메타데이터를 읽거나 수정하는 패키지라고 한다.

Kotlin K2 컴파일러

코틀린의 새로운 컴파일러 K2가 안정화되어 출시되었다고 한다. 코틀린 2부턴 기본적으로 이 컴파일러를 쓰며 성능도 좋고 특히 KMP에서 쩐다고한다.

참 코틀린의 변화를 지켜보면, Jetbrains는 일을 굉장히 잘하는것같다.

K2 Migration Guide도 마련되어있다.

특히 아키텍쳐에 대해서 강조하는데,

  • call resolution 및 type inference 개선
  • 새로운 기능 도입에 대한 손쉬운 sugaring
  • 빠른 컴파일
  • IDE에서도 굳굳

등이 있다고 한다.

Smart cast improvements

난 지금까지도 코틀린이 굉장히 똑똑한 언어라고 생각했는데 더 똑똑해진것같다.

대략 다음과 같이 자동 형변환이 개선되었다고 한다.

이 내용들은 정리는 해놨지만 자세히 몰라도 된다. 그냥 IDE와 컴파일러를 믿고 더 편해졌네 정도를 직접 코딩하면서 느낄 수 있으면 된다.

Local variables and further scopes

대략 이런 느낌으로 if 밖에 선언된 변수의 타입 추론의 결과도 더 똑똑하게 추론해준다는 의미같다.

fun petAnimal(animal: Any) {
    val isCat = animal is Cat
    if (isCat) {
        // In Kotlin 2.0.0, the compiler can access
        // information about isCat, so it knows that
        // animal was smart-cast to the type Cat.
        // Therefore, the purr() function can be called.
        // In Kotlin 1.9.20, the compiler doesn't know
        // about the smart cast, so calling the purr()
        // function triggers an error.
        animal.purr()
    }
}

Type checks with logical or operator

|| 연산자에서 타입을 체크하는 is 가 여러개 있을 때 if문 을 통과한다면 Lower Common Ancestor 타입으로 형변환이 된다고 한다.

원래는 Any로 되었다고 한다.

fun signalCheck(signalStatus: Any) {
    if (signalStatus is Postponed || signalStatus is Declined) {
        // signalStatus is smart-cast to a common supertype Status
        signalStatus.signal()
        // Prior to Kotlin 2.0.0, signalStatus is smart cast
        // to type Any, so calling the signal() function triggered an
        // Unresolved reference error. The signal() function can only
        // be called successfully after another type check:

        // check(signalStatus is Status)
        // signalStatus.signal()
    }
}

Inline functions

인라인 함수는 이제 callsInPlace로 취급되어 인라인 함수의 람다 인자가 항상 호출 시점에 불릴 것이므로(non-escaping) 참조누수가 없다는 것을 컴파일러가 인지할 수 있게 되었다.

따라서 이 정보를 바탕으로 컴파일러는 인라인 함수의 람다 인자 내부에서 다른 변수가 누수되지 않음을 알기 때문에 쓸데없는 옵셔널 체이닝이 줄어들게 되었다.

interface Processor {
    fun process()
}

inline fun inlineAction(f: () -> Unit) = f()

fun nextProcessor(): Processor? = null

fun runProcessor(): Processor? {
    var processor: Processor? = null
    inlineAction {
        // In Kotlin 2.0.0, the compiler knows that processor
        // is a local variable, and inlineAction() is an inline function, so
        // references to processor can't be leaked. Therefore, it's safe
        // to smart-cast processor.

        // If processor isn't null, processor is smart-cast
        if (processor != null) {
            // The compiler knows that processor isn't null, so no safe call
            // is needed
            processor.process()

            // In Kotlin 1.9.20, you have to perform a safe call:
            // processor?.process()
        }

        processor = nextProcessor()
    }

    return processor
}

properties with function types

코틀린 2 전에는 class의 properties가 함수 타입이면 smart-cast가 되지 않던 버그가 있었는데 고쳐졌다고 한다.

Exception handling

smart cast의 정보가 catch, finally에도 잘 전달이 된다고 한다.

Increment and decrement operators

++-- 연산자 이후 객체의 타입이 변환될 수 있음을 컴파일러가 이해할 수 없었으나 이제 가능하다.

Kotlin Multiplatform Improvements

난 솔직히 KMP에 대해서 잘 모르고 써본적도 없다. 주로 안드로이드 개발을 위해 JVM에서의 코틀린을 사용해보았을 뿐이라서 해당 내용은 스킵한다.

IDE에서 K2를 활성화하는 법

다음과 같이 활성화할 수 있다.

In your IDE, go to Settings   Languages & Frameworks   Kotlin and select the Enable K2 mode option. The IDE will analyze your code using its K2 mode.

Categories:

Updated:

Comments