TIL

k2 compiler

image

K2 컴파일러는 Kotlin 2.0.0부터 기본적으로 활성화된다.

Performance improvements

Language feature improments

Smart casts

class Cat {
    fun purr() {
        println("Purr purr")
    }
}

fun petAnimal(animal: Any) {
    val isCat = animal is Cat
    if (isCat) {
        // Kotlin 2.0.0에선 컴파일러가 isCat에 접근할 수 있기에
        // animal을 스마트 캐스팅할 수 있다.
        // 때문에 purr() 메서드를 호출할 수 있는 것
        animal.purr() // smart casting!
    }
}

fun main(){
    val kitty = Cat()
    petAnimal(kitty)
    // Purr purr
}
interface Status {
    fun signal() {}
}

interface Ok : Status
interface Postponed : Status
interface Declined : Status

fun signalCheck(signalStatus: Any) {
    if (signalStatus is Postponed || signalStatus is Declined) {
        // signalStatus 변수가 두 타입의 공통 상위 타입인 Status로 캐스팅된다.
        signalStatus.signal() // signal()을 호출할 수 있게 된다.
    }
}
interface Processor {
    fun process()
}

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

fun nextProcessor(): Processor? = null

fun runProcessor(): Processor? {
    var processor: Processor? = null
    inlineAction {
        // Kotlin 2.0.0에선 컴파일러가 processor가 지역 변수이고 inlineAction이
        // 인라인 함수임을 알기에 processor가 유출될 수 없음을 안다.
        // 그러므로 processor로 스마트 캐스트하는 것이 안전하다.

        if (processor != null) {
            // null이 아님을 알았다면 safe-call을 할 필요 없이
            // 스마트 캐스팅 되어 함수를 호출할 수 있다.
            processor.process()

            // 이전에는 processor?.process()로 사용해야 했다.
        }

        processor = nextProcessor()
    }

    return processor
}
class Holder(val provider: (() -> Unit)?) {
    fun process() {
        if (provider != null) {
            // 컴파일러가 provider가 null이 아님을 안다.
            provider()

            // 이전 버전에서는 safe-call을 해야 했다.
        }
    }
}
fun testString() {
    var stringInput: String? = null
    // 값을 할당함으로써 non-null String 타입으로 스마트 캐스팅
    stringInput = ""
    try {
        // 컴파일러는 null이 아님을 안다.
        println(stringInput.length)
        // 0

        // 컴파일러는 다시 String? 타입으로 인식
        stringInput = null

        // 예외를 발생시키기
        if (2 > 1) throw Exception()
        stringInput = ""
    } catch (exception: Exception) {
        // Kotlin 2.0.0에서 stringInput이 nullable임을 알아서 safe-call 수행
        println(stringInput?.length)
        // null

        // Kotlin 1.9.20에선 safe-call이 필요하지 않다고 말할 것이다.
    }
}
interface Rho {
    operator fun inc(): Sigma = TODO()
}

interface Sigma : Rho {
    fun sigma() = Unit
}

interface Tau {
    fun tau() = Unit
}

fun main(input: Rho) {
    var unknownObject: Rho = input

    // unknownObject는 Rho와 Tau 타입이 모두 될 수 있다.
    if (unknownObject is Tau) {

        // Rho 타입으로써 inc 증가 연산자를 호출
        // Kotlin 2.0.0에선 Sigma 타입으로 스마트 캐스팅 된다.
        ++unknownObject

        // 때문에 Sigma 타입의 sigma() 함수를 성공적으로 호출한다.
        unknownObject.sigma()
        
        // In Kotlin 2.0.0에선 컴파일러기 unknownObjet를 Sigma 타입이라
        // 알고 있기에 아래 구문은 컴파일 에러가 발생한다.
        unknownObject.tau()

        // In Kotlin 1.9.20에선 컴파일 에러가 발생하지 않고 런타임에 
        // ClassCastException이 발생한다.
    }
}

Kotlin Multiplatform

K2 컴파일러에는 다음과 같은 영역에서 Kotlin 멀티플랫폼 관련 개선 사항이 있다.

공통 소스와 플랫폼 소스 분리

// Commmon code
fun foo(x: Any) = println("common foo")

fun exampleFunction() {
  foo(42)
}
// Platform code
// JVM
fun foo(x: Int) = println("platform foo")

// JavaScript
// 자바스크립트 플랫폼에는 foo() 함수 오버로드가 없다.

예측 선언과 실제 선언의 서로 다른 가시성 수준

expect internal class Attribute // internal
actual class Attribute          // public

https://kotlinlang.org/docs/k2-compiler-migration-guide.html