Kotlin 2.0

Arakene·2024년 10월 10일

Smart Cast

스마트 캐스트는 조건문 등을 통해 변수가 자동으로 변환되는 것을 말한다. if 문에서 Null check를 했다면 if문의 스코프 내부에서는 not null로 자동 변환되는걸 예시로 들 수 있다.

Local variables and further scopes

변경점

2.0 이전에는 if문 외부에서 is casting을 하면 기존에는 if문 스코프에서는 if condition에 대한 정보가 없어 smart-case가 실패해서 관련함수를 부를 수 없었다.
하지만 2.0부터는 if, when, while 외부에서 조건문에 사용할 변수를 선언해도 접근가능한 스코프에 맞는 타입으로 스마트 캐스팅을 해준다.
이런 변경점은 boolean condition을 변수로 빼내서 코드의 가독성과 재사용성을 높일 수 있다.

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

fun petAnimal(animal: Any) {
    val isCat = animal is Cat
    if (isCat) {
     	// 2.0 이전에는 해당 함수를 부를 수 없음
        // 2.0 부터는 스마트 캐스팅을 해줌
        animal.purr()
    }
}

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

Type checks with logical or operator

변경점

or operator(||)을 통해 오브젝트 타입 체크를 할 때 기존에는 Any 타입으로 항상 캐스팅되어서 슈퍼타입의 함수를 호출할 수 없었지만 2.0부터는 가장 가까운 공통 슈퍼타입으로 캐스팅된다.

interface Status {
    fun signal() {}
}

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

fun signalCheck(signalStatus: Any) {
    if (signalStatus is Postponed || signalStatus is Declined) {
    	// 2.0 이전에는 해당 함수를 호출하면 Unresolved reference error가 반겨준다.
        // 2.0부터는 슈퍼타입인 Status로 스마트 캐스팅을 해주기에 호출가능
        signalStatus.signal()
        // 2.0 이전이라면 아래와 같이 한번 더 타입체크를 해줘야 정상적으로 signal()를 호출할 수 있다.
        // check(signalStatus is Status)
        // signalStatus.signal()
    }
}

Power-assert plugin

테스트 실패 메세지에 더 다양한 정보를 포함시켜 보여주는 플러그인으로 아직은 experimental 단계이다.

주요기능

  • 향상된 에러 메세지
  • 테스트 단순화
  • multiple function 지원

KMP

변경점

  • Separation of common and platform sources during compilation
  • Different visibility levels of expected and actual declarations

Stability Configuration

참고문서
String Skipping Mode는 컴포지션 발생 시 인풋이 달라졌다는 걸 두가지 방법으로 체크한다.

  • stable 클래스면 structural equality(.equals())
  • unstable 클래스면 referential equality(===)
    만약 unstable한 클래스에 대해서 컴포지션이 발생한 경우 이전 컴포지션에서와 동일한 인스턴스면 스킵하게된다.

configuration file로 stability 관리하기

기존에는 @Immutable같은 어노테이션을 활용하거나 래핑클래스를 만들고 해당 클래스에 어노테이션을 추가, Immutable Collection을 사용하는 방법이 있었다.
java.time.LocalDate 처럼 코드를 수정할 수 없는 경우에는 래퍼를 만드는 방법말고는 없었는데 이제는 config 파일을 통해 해당 파일에서 지정한건 모두 stable로 판단될 수 있다.
composeCompiler에 root 단에 생성한 설정 파일을 지정해주면된다.

composeCompiler {
  ...

  // Set path of the config file
  stabilityConfigurationFile = rootProject.file("stability_config.conf")
}

해당 방법을 사용하면 도메인 단에 있는 클래스들도 설정이 가능하니 프레젠테이션과 도메인단의 매퍼를 제거할 수도 있다.

// stabilize all classes in model package
com.example.app.domain.model.*

lambda 관리가 쉬워짐

strong skipping ahemdml 다른 이점으로는 unstable한 캡처임에도 모든 람다는 remember 된다. 예전에는 뷰모델의 함수를 넘기는 경우 컴포지션을 발생시킬 수도 있어 람다를 remember 했는데 이제는 안해도 된다. 컴포즈 컴파일러가 자동으로 remember 해준다.

그렇다면 Immutable Collection 불필요한거아닌가?

위에서 설명한 stability configuration에 기존의 컬랙션들을 추가하면 컬랙션 또한 stable로 간주될텐데 그럼 Immutable Collection이 필요없는가 할 수 있지만 아니다.
경우에 따라서는 그냥 List보다 더 느려지는 경우가 있다.
List를 설정파일에 추가하면 리스트 내부의 모든 아이템에 대해서 equals콜이 이루어지게된다. 만약 lazyList 환경이라면 더 많은 횟수의 equals가 발생하게 된다.
따라서 적용하기전에 한번 Macrobenchmark를 돌려 확인해 보는게 좋다고 한다.

profile
안녕하세요 삽질하는걸 좋아하는 4년차 안드로이드 개발자입니다.

0개의 댓글