본문 바로가기
Kotlin/What's new

[Kotlin 번역] What's new in Kotlin 1.4.0

by 노력남자 2023. 9. 16.
반응형

2020년 8월 17일

 

Kotlin 1.4.0에서는 모든 구성 요소에서 품질과 성능에 중점을 둔 다양한 개선 사항을 제공합니다. 아래에서는 Kotlin 1.4.0에서 가장 중요한 변경 사항 목록을 찾을 수 있습니다.


언어 기능과 개선 사항


Kotlin 1.4.0은 다양한 언어 기능과 개선 사항을 포함하고 있습니다. 이들은 다음과 같습니다:

  • Kotlin 인터페이스에 대한 SAM 변환
  • 라이브러리 작성자를 위한 명시적 API 모드
  • 이름 지정 및 위치 지정 인수 혼용
  • 마지막 쉼표
  • 호출 가능한 참조 개선
  • 루프 내부에 포함된 break 및 continue


Kotlin 인터페이스에 대한 SAM 변환


Kotlin 1.4.0 이전에는 Kotlin 인터페이스에서는 SAM (단일 추상 메서드) 변환이 Java 메서드 및 Java 인터페이스와 함께 작업할 때만 적용할 수 있었습니다. 이제 Kotlin 인터페이스에 대한 SAM 변환도 사용할 수 있습니다. 이를 위해 Kotlin 인터페이스를 fun 수정자와 함께 명시적으로 함수 형식으로 표시하세요.

SAM 변환은 한 인터페이스에 하나의 단일 추상 메서드만 있는 경우 람다를 매개변수로 전달할 때 적용됩니다. 이 경우 컴파일러는 자동으로 람다를 추상 멤버 함수를 구현하는 클래스의 인스턴스로 변환합니다.

 

fun interface IntPredicate {
    fun accept(i: Int): Boolean
}

val isEven = IntPredicate { it % 2 == 0 }

fun main() { 
    println("Is 7 even? - ${isEven.accept(7)}")
}

 

SAM Interfaces에 대해선 여기서 더 알아보세요.


라이브러리 작성자를 위한 명시적 API 모드


Kotlin 컴파일러는 라이브러리 작성자를 위한 명시적 API 모드를 제공합니다. 이 모드에서 컴파일러는 라이브러리의 API를 더 명확하고 일관성 있게 만드는 데 도움이 되는 추가적인 검사를 수행합니다. 다음과 같은 요구 사항을 라이브러리의 공개 API에 노출된 선언에 추가합니다.

  • 선언이 기본 가시성으로 공개 API에 노출되는 경우 가시성 수정자가 필요합니다. 이를 통해 선언이 의도치 않게 공개 API에 노출되지 않도록 합니다.
  • 공개 API에 노출되는 속성 및 함수에 대해 명시적인 유형 지정이 필요합니다. 이를 통해 API 사용자가 사용하는 API 멤버의 유형을 인식할 수 있도록 보장합니다.

 

구성에 따라 이러한 명시적 API는 오류 (엄격 모드) 또는 경고 (경고 모드)를 생성할 수 있습니다. 가독성과 일반적인 이해를 위해 특정 유형의 선언은 이러한 검사에서 제외됩니다.

  • 주 생성자
  • 데이터 클래스의 속성
  • 속성 getter 및 setter
  • 재정의 메서드

 

명시적 API 모드는 모듈의 생성 소스만 분석합니다.

모듈을 명시적 API 모드로 컴파일하려면 Gradle 빌드 스크립트에 다음 줄을 추가하세요.

kotlin {
    // 엄격 모드를 위한 설정
    explicitApi()
    // 또는
    explicitApi = ExplicitApiMode.Strict

    // 경고 모드를 위한 설정
    explicitApiWarning()
    // 또는
    explicitApi = ExplicitApiMode.Warning
}


명령 줄 컴파일러를 사용하는 경우 -Xexplicit-api 컴파일러 옵션에 strict 또는 warning 값을 추가하여 명시적 API 모드로 전환할 수 있습니다.

 

-Xexplicit-api={strict|warning}


명시적 API 모드에 대한 자세한 내용은 KEEP에서 확인하세요.


이름 지정 및 위치 지정 인수 혼용


Kotlin 1.3에서 이름 지정 인수로 함수를 호출할 때 이름 없는 (위치 지정 인수) 모든 인수를 첫 번째 이름 지정 인수 앞에 배치해야 했습니다. 예를 들어, f(1, y = 2)를 호출할 수 있었지만 f(x = 1, 2)를 호출할 수 없었습니다.

모든 인수가 올바른 위치에 있을 때 중간에 하나의 인수에 이름을 지정하려는 경우 이는 정말 귀찮은 일이었습니다. 특히 boolean 또는 null 값이 어떤 속성에 속하는지 명확히하려는 데 도움이 되었습니다.

Kotlin 1.4에서는 이러한 제한이 없어졌습니다. 이제 위치 지정 인수 집합 중간에 인수에 이름을 지정할 수 있습니다. 또한 위치 지정 및 이름 지정 인수를 원하는 대로 혼용할 수 있지만 올바른 순서대로 유지되어야 합니다.

fun reformat(
    str: String,
    uppercaseFirstLetter: Boolean = true,
    wordSeparator: Char = ' '
) {
    // ...
}

// 중간에 이름 지정된 인수로 함수 호출
reformat("This is a String!", uppercaseFirstLetter = false , '-')

 

트레일링 콤마

 

Kotlin 1.4 버전에서는 열거형 (enumerations)에서 trailing comma를 추가할 수 있습니다. 이것은 인수 및 매개변수 목록, 항목 (entries) 및 구조 분해 선언의 구성 요소와 같은 경우에 적용할 수 있습니다. trailing comma를 사용하면 쉼표를 추가하거나 제거하지 않고도 새 항목을 추가하고 순서를 변경할 수 있습니다.

특히 매개변수나 값에 대한 다중 라인 구문을 사용하는 경우에 유용합니다. trailing comma를 추가한 후에는 매개변수나 값의 라인을 쉽게 교체할 수 있습니다.

 

fun reformat(
    str: String,
    uppercaseFirstLetter: Boolean = true,
    wordSeparator: Character = ' ', // trailing comma
) {
    // ...
}
val colors = listOf(
    "red",
    "green",
    "blue", // trailing comma
)


Callable reference 개선


Kotlin 1.4에서는 callable reference를 사용하는 경우 더 많은 경우를 지원합니다.

  • 기본 인수 값을 가진 함수에 대한 함수 참조
  • Unit 반환 함수에서 함수 참조
  • 함수의 인수 수에 따라 적응하는 참조
  • callable reference에 대한 suspend 변환

 

기본 인수 값을 가진 함수에 대한 함수 참조

 

이제 기본 인수 값을 가진 함수에 대한 callable reference를 사용할 수 있습니다. 함수 foo에 대한 callable reference가 인수를 가지지 않는 경우 기본 값 0이 사용됩니다.

 

fun foo(i: Int = 0): String = "$i!"

fun apply(func: () -> String): String = func()

fun main() {
    println(apply(::foo))
}


이전에는 기본 인수 값을 사용하려면 함수 apply에 대한 추가 오버로드를 작성해야 했습니다.

 

// 일부 새로운 오버로드
fun applyInt(func: (Int) -> String): String = func(0)


Unit 반환 함수에서 함수 참조


Kotlin 1.4에서는 Unit 반환 함수에서 어떤 유형의 함수든 callable reference를 사용할 수 있습니다. Kotlin 1.4 이전에는 이 경우에는 람다 인수만 사용할 수 있었습니다. 이제 람다 인수와 callable reference를 모두 사용할 수 있습니다.

 

fun foo(f: () -> Unit) { }
fun returnsInt(): Int = 42

fun main() {
    foo { returnsInt() } // 이것은 1.4 이전에 사용하는 유일한 방법이었습니다.
    foo(::returnsInt) // 1.4부터는 이것도 작동합니다.
}


함수의 인수 수에 따라 적응하는 참조


이제 변수 수의 인수 (vararg)를 전달할 때 함수에 대한 callable reference를 적응시킬 수 있습니다. 전달된 인수 목록의 끝에 동일한 유형의 매개변수를 원하는 수만큼 전달할 수 있습니다.

 

fun foo(x: Int, vararg y: String) {}

fun use0(f: (Int) -> Unit) {}
fun use1(f: (Int, String) -> Unit) {}
fun use2(f: (Int, String, String) -> Unit) {}

fun test() {
    use0(::foo)
    use1(::foo)
    use2(::foo)
}


callable reference에 대한 suspend 변환


Kotlin은 이제 버전 1.4.0부터 callable reference에 대한 suspend 변환을 지원합니다.

 

fun call() {}
fun takeSuspend(f: suspend () -> Unit) {}

fun test() {
    takeSuspend { call() } // 1.4 이전에도 작동
    takeSuspend(::call) // Kotlin 1.4에서도 작동
}


루프 내의 when 식에 포함된 break 및 continue 사용


Kotlin 1.3에서는 루프에 포함된 when 식 내에서 라벨이 없는 break 및 continue를 사용할 수 없었습니다. 그 이유는 when 식 내에서 fall-through 동작이 가능하도록 예약되어 있던 키워드이기 때문입니다.

따라서 루프에 포함된 when 식 내에서 break 및 continue를 사용하려면 라벨을 붙여야 했으며 이는 다소 불편했습니다.

 

fun test(xs: List<Int>) {
    LOOP@for (x in xs) {
        when (x) {
            2 -> continue@LOOP
            17 -> break@LOOP
            else -> println(x)
        }
    }
}


Kotlin 1.4에서는 루프에 포함된 when 식 내에서 라벨 없이도 break 및 continue를 사용할 수 있습니다. 이러한 키워드는 가장 가까운 루프를 종료하거나 다음 단계로 진행하도록 예상대로 동작합니다.

 

fun test(xs: List<Int>) {
    for (x in xs) {
        when (x) {
            2 -> continue
            17 -> break
            else -> println(x)
        }
    }
}


when 식 내의 fall-through 동작은 추가 설계에 따라 다릅니다.

 

IDE에 새로운 기능

 

Kotlin 1.4 버전에서는 IntelliJ IDEA의 새로운 도구를 사용하여 Kotlin 개발을 보다 간편하게 할 수 있습니다.

  • 유연한 새 프로젝트 마법사
  • 코루틴 디버거


유연한 새 프로젝트 마법사


새로운 Kotlin 프로젝트 마법사를 사용하면 다양한 유형의 Kotlin 프로젝트를 쉽게 생성하고 구성할 수 있습니다. 이러한 프로젝트 중에는 UI 없이 구성하기 어려운 멀티플랫폼 프로젝트도 포함됩니다.

 


새로운 Kotlin 프로젝트 마법사는 간단하면서도 유연합니다.

1. 무엇을 하려는지에 따라 프로젝트 템플릿을 선택합니다. 미래에는 더 많은 템플릿이 추가될 것입니다.

2. 빌드 시스템을 선택합니다. Gradle (Kotlin 또는 Groovy DSL), Maven 또는 IntelliJ IDEA를 선택할 수 있습니다.

Kotlin 프로젝트 마법사는 선택한 프로젝트 템플릿에서 지원하는 빌드 시스템만 표시합니다.

3.메인 화면에서 프로젝트 구조를 미리 확인합니다.

 

프로젝트 생성을 완료하거나 선택적으로 다음 화면에서 프로젝트를 구성합니다.

 

4. 이 화면에서 이 프로젝트 템플릿을 지원하는 모듈과 대상을 추가/제거할 수 있습니다.

5. 모듈 및 대상 설정, 예를 들어 대상 JVM 버전, 대상 템플릿 및 테스트 프레임워크를 구성할 수 있습니다.

 

 

미래에는 Kotlin 프로젝트 마법사를 더 유연하게 만들기 위해 더 많은 구성 옵션과 템플릿을 추가할 예정입니다.

새로운 Kotlin 프로젝트 마법사를 사용해보려면 다음 튜토리얼을 참고할 수 있습니다.

 

코루틴 디버거


많은 사람들이 비동기 프로그래밍을 위해 코루틴을 이미 사용하고 있습니다. 그러나 Kotlin 1.4 이전에 코루틴을 디버깅하는 것은 어려운 경우가 많았습니다. 코루틴은 스레드 간에 이동하므로 특정 코루틴이 무엇을 수행하고 있는지 이해하고 해당 콘텍스트를 확인하는 것이 어려웠습니다. 일부 경우에는 중단점에서 단계를 추적하는 것이 간단히 동작하지 않기도 했습니다. 결과적으로 코루틴을 사용하는 코드를 디버깅할 때 로깅이나 정신적인 노력을 의존해야 했습니다.

Kotlin 1.4에서는 Kotlin 플러그인과 함께 제공되는 새로운 기능으로 코루틴을 디버깅하는 것이 훨씬 편리해졌습니다.

코루틴 디버깅은 kotlinx-coroutines-core의 버전 1.3.8 이상에서 작동합니다.

디버그 도구 창에는 이제 코루틴 탭이 추가되었습니다. 이 탭에서는 현재 실행 중인 코루틴과 중단된 코루틴에 대한 정보를 찾을 수 있습니다. 코루틴은 실행 중인 디스패처에 따라 그룹화됩니다.

 


이제 다음을 수행할 수 있습니다.

각 코루틴의 상태를 쉽게 확인합니다.

실행 중인 코루틴과 중단된 코루틴의 로컬 및 캡처된 변수 값들을 확인합니다.

전체 코루틴 생성 스택과 코루틴 내부의 호출 스택을 확인합니다. 이 스택에는 표준 디버깅 중에 손실될 수 있는 모든 프레임과 변수 값이 포함됩니다.

 

각 코루틴의 상태와 스택에 대한 전체 보고서가 필요한 경우 코루틴 탭 내에서 마우스 오른쪽 버튼을 클릭하고 "코루틴 덤프 가져오기"를 선택할 수 있습니다. 현재 코루틴 덤프는 비교적 간단하지만 향후 Kotlin 버전에서 더 가독성 있고 유용한 형태로 개선될 예정입니다.

 


코루틴 디버깅에 대한 자세한 내용은 블로그 포스트와 IntelliJ IDEA 설명서에서 확인할 수 있습니다.

 

새로운 컴파일러

 

새로운 Kotlin 컴파일러는 매우 빠를 것으로 예상되며, 모든 지원 플랫폼을 통합하고 컴파일러 확장을 위한 API를 제공할 것입니다. 이는 장기 프로젝트로, Kotlin 1.4.0에서 이미 몇 가지 단계를 완료했습니다.

  • 더 강력한 타입 추론 알고리즘
  • 새로운 JVM 및 JS IR 백엔드

 

더 강력한 타입 추론 알고리즘


Kotlin 1.4에서는 새로운 더 강력한 타입 추론 알고리즘을 사용합니다. 이 새로운 알고리즘은 컴파일러 옵션을 지정하여 Kotlin 1.3에서 이미 시도할 수 있었으며 이제는 기본값으로 사용됩니다. 이 새로운 알고리즘에서 수정된 이슈 목록 전체를 YouTrack에서 찾을 수 있습니다. 여기에 몇 가지 주목할 만한 개선 사항이 있습니다.

  • 자동으로 타입을 추론할 수 있는 더 많은 경우
  • 람다의 마지막 표현식에 대한 스마트 캐스트
  • 호출 가능한 참조에 대한 스마트 캐스트
  • 위임된 프로퍼티에 대한 더 나은 추론
  • 다른 인수를 가진 Java 인터페이스에 대한 SAM 변환
  • Kotlin에서의 Java SAM 인터페이스

자동으로 타입을 추론할 수 있는 더 많은 경우


새로운 추론 알고리즘은 이전 알고리즘이 명시적으로 타입을 지정해야 하는 많은 경우에 대해 타입을 자동으로 추론합니다. 예를 들어 다음 예제에서는 람다 매개변수 it의 타입이 올바르게 String?로 추론됩니다.

 

val rulesMap: Map<String, (String?) -> Boolean> = mapOf(
    "weak" to { it != null },
    "medium" to { !it.isNullOrBlank() },
    "strong" to { it != null && "^[a-zA-Z0-9]+$".toRegex().matches(it) }
)


이전에는 이 작업을 수행하려면 명시적으로 람다 매개변수를 도입하거나 Pair 생성자를 사용하여 명시적인 제네릭 인자를 가진 생성자로 바꾸어야 했습니다.


람다의 마지막 표현식에 대한 스마트 캐스트


Kotlin 1.3에서 람다 내부의 마지막 표현식은 기대되는 타입을 명시적으로 지정하지 않는 한 스마트 캐스트되지 않았습니다. 따라서 다음 예제에서 Kotlin 1.3은 결과 변수 result의 타입으로 String?을 추론합니다.

 

val result = run {
    var str = currentValue()
    if (str == null) {
        str = "test"
    }
    str // Kotlin 1.3에서는 str이 null이 아님을 알 수 있음
}


새로운 추론 알고리즘 덕분에 Kotlin 1.4에서는 람다 내부의 마지막 표현식이 스마트 캐스트되며, 이 새로운 보다 정확한 타입이 결과 람다 타입을 추론하는 데 사용됩니다. 따라서 결과 변수 result의 타입은 String이 됩니다.

Kotlin 1.3에서는 이러한 경우를 작동하도록 만들려면 명시적인 캐스트 (예: !! 또는 as String과 같은 타입 캐스트)를 추가해야 했지만 이제 이러한 캐스트가 불필요해졌습니다.


호출 가능한 참조에 대한 스마트 캐스트


Kotlin 1.3에서는 스마트 캐스트 타입의 멤버 참조에 액세스할 수 없었습니다. 그러나 Kotlin 1.4에서는 가능합니다.

 

fun perform(animal: Animal) {
    val kFunction: KFunction<*> = when (animal) {
        is Cat -> animal::meow
        is Dog -> animal::woof
    }
    kFunction.call()
}


타입 검사 후, animal 변수를 특정 타입인 Cat 및 Dog로 스마트 캐스트한 후에 animal::meow 및 animal::woof와 같은 멤버 참조를 사용할 수 있습니다. 타입 확인 이후에 하위 타입에 해당하는 멤버 참조에 액세스할 수 있습니다.


위임된 프로퍼티에 대한 더 나은 추론


위임된 프로퍼티의 타입은 by 키워드 뒤에 따르는 대리자 표현식을 분석할 때 고려되지 않았습니다. 예를 들어 다음 코드는 이전에 컴파일되지 않았지만 이제는 올바르게 이전 및 새 매개변수의 타입을 String?으로 추론합니다.

 

import kotlin.properties.Delegates

fun main() {
    var prop: String? by Delegates.observable(null) { p, old, new ->
        println("$old → $new")
    }
    prop = "abc"
    prop = "xyz"
}


다른 인수를 가진 Java 인터페이스에 대한 SAM 변환


Kotlin은 Java 인터페이스에 대한 SAM 변환을 지원했지만, 기존의 Java 라이브러리를 사용할 때 때로는 지원되지 않는 경우가 있었습니다. Java 메서드를 호출하여 두 개의 SAM 인터페이스를 매개변수로 전달하는 경우, 두 인수 모두 람다 또는 일반 객체 여야 했습니다. 람다로 하나의 인수를 전달하고 다른 하나를 객체로 전달할 수 없었습니다.

새로운 알고리즘은 이 문제를 해결하고 어떤 경우에도 SAM 인터페이스 대신 람다를 전달할 수 있게 되었습니다.

 

// FILE: A.java
public class A {
    public static void foo(Runnable r1, Runnable r2) {}
}

// FILE: test.kt
fun test(r1: Runnable) {
    A.foo(r1) {}  // Kotlin 1.4에서 작동
}

 

코틀린에서의 Java SAM Interface


Kotlin 1.4에서 Java SAM 인터페이스를 Kotlin에서 사용하고 SAM 변환을 적용할 수 있습니다.

 

import java.lang.Runnable

fun foo(r: Runnable) {}

fun test() {
    foo { } // OK
}


Kotlin 1.3에서는 SAM 변환을 수행하려면 위의 함수 foo를 Java 코드에서 선언해야 했습니다.


통합된 백엔드 및 확장성


Kotlin에서는 실행 가능한 코드를 생성하는 세 가지 백엔드가 있습니다: Kotlin/JVM, Kotlin/JS 및 Kotlin/Native. Kotlin/JVM과 Kotlin/JS는 서로 독립적으로 개발되어 코드를 거의 공유하지 않았습니다. Kotlin/Native는 Kotlin 코드에 대한 중간 표현(IR)을 기반으로 한 새로운 인프라를 기반으로 하고 있습니다.

현재 Kotlin/JVM과 Kotlin/JS를 동일한 IR로 마이그레이션 중입니다. 결과적으로 모든 세 백엔드가 많은 로직을 공유하고 통합된 파이프라인을 가지게 됩니다. 이로써 대부분의 기능, 최적화 및 버그 수정을 모든 플랫폼에 대해 한 번만 구현할 수 있습니다. 두 개의 새로운 IR 기반 백엔드는 Alpha 상태입니다.

공통 백엔드 인프라는 멀티플랫폼 컴파일러 확장을 위한 문을 엽니다. 파이프라인에 연결하고 모든 플랫폼에서 자동으로 작동하는 사용자 지정 처리 및 변환을 추가할 수 있게 됩니다.

현재 Alpha 상태인 새로운 JVM IR 및 JS IR 백엔드를 사용하고 피드백을 공유하도록 권장합니다.

 

Kotlin/JVM

 

Kotlin 1.4.0은 여러 가지 JVM 특화 개선 사항을 포함하고 있습니다. 이에는 다음과 같은 내용이 포함됩니다:

  • 새로운 JVM IR 백엔드
  • 인터페이스에서 기본 메서드 생성을 위한 새로운 모드
  • 널 체크를 위한 통합 예외 유형
  • JVM 바이트코드에서의 타입 어노테이션

 

새로운 JVM IR 백엔드


Kotlin/JS와 함께 Kotlin/JVM도 통합 IR 백엔드로 이동하고 있으며, 이로써 대부분의 기능과 버그 수정을 모든 플랫폼에 대해 한 번만 구현할 수 있게 됩니다. 또한 모든 플랫폼에서 작동하는 멀티플랫폼 확장을 생성하여 이를 활용할 수 있습니다.

현재 Kotlin 1.4.0은 이러한 확장을 위한 공개 API를 제공하지 않지만, Jetpack Compose를 포함한 파트너와 긴밀하게 협력하고 있으며, 이들은 이미 새로운 백엔드를 사용하여 컴파일러 플러그인을 빌드하고 있습니다.

새로운 Kotlin/JVM 백엔드를 시도하고, 이에 대한 문제 및 기능 요청을 이슈 트래커에 제출하는 것을 권장합니다. 이를 통해 컴파일러 파이프라인을 통합하고 Jetpack Compose와 같은 컴파일러 확장을 Kotlin 커뮤니티에 더 빨리 제공할 수 있게 될 것입니다.

새로운 JVM IR 백엔드를 활성화하려면 Gradle 빌드 스크립트에서 다음과 같은 추가 컴파일러 옵션을 지정하십시오:

 

kotlinOptions.useIR = true


Jetpack Compose를 활성화하면 kotlinOptions에서 컴파일러 옵션을 지정하지 않아도 자동으로 새로운 JVM 백엔드로 전환됩니다.

명령줄 컴파일러를 사용하는 경우, 컴파일러 옵션 -Xuse-ir을 추가하십시오.

새로운 JVM IR 백엔드를 활성화하지 않은 경우 새로운 백엔드로 컴파일된 코드를 사용할 수 없습니다. 이를 고려하여 라이브러리 작성자들은 제품에서 새로운 백엔드로 전환하지 않는 것을 권장합니다.


인터페이스의 기본 메서드 생성을 위한 새로운 모드


Kotlin 코드를 JVM 1.8 이상의 타겟으로 컴파일할 때, Kotlin 인터페이스의 비추상 메서드를 Java의 기본 메서드로 컴파일할 수 있습니다. 이를 위해 @JvmDefault 어노테이션을 사용하여 이러한 메서드를 표시하고, -Xjvm-default 컴파일러 옵션을 사용하여 이 어노테이션을 처리할 수 있도록 메커니즘이 제공되었습니다.

1.4.0에서는 기본 메서드 생성을 위한 새로운 모드가 추가되었습니다. -Xjvm-default=all은 Kotlin 인터페이스의 모든 비추상 메서드를 기본 Java 메서드로 컴파일합니다. 기본 메서드를 사용하지 않고 컴파일된 인터페이스를 사용하는 코드와의 호환성을 위해 all-compatibility 모드도 추가되었습니다.

Java 상호 운용성에 대한 자세한 내용은 상호 운용성 문서와 이 블로그 포스트를 참조하십시오.


널 체크를 위한 통합 예외 유형


Kotlin 1.4.0부터 모든 런타임 널 체크는 KotlinNullPointerException, IllegalStateException, IllegalArgumentException 및 TypeCastException 대신  java.lang.NullPointerException을 throw합니다. 이는 !! 연산자, 메서드 전처리에서의 매개변수 널 체크, 플랫폼 타입 표현식 널 체크, 비-nullable 타입과 함께 사용하는 as 연산자에 적용됩니다. 이는 lateinit 널 체크나 checkNotNull 또는 requireNotNull과 같은 명시적 라이브러리 함수 호출에는 적용되지 않습니다.

이 변경 사항은 Kotlin 컴파일러 또는 Android R8 옵티마이저와 같은 다양한 종류의 바이트코드 처리 도구에 의해 수행 가능한 널 체크 최적화 옵션의 수를 늘립니다.

개발자 관점에서 보면, 변화는 그다지 크지 않습니다. Kotlin 코드는 이전과 동일한 오류 메시지와 함께 예외를 throw하지만 예외 유형이 변경되었지만 전달되는 정보는 그대로 유지됩니다.


JVM 바이트코드에서의 타입 어노테이션


Kotlin은 이제 JVM 바이트코드 (타겟 버전 1.8+)에서 타입 어노테이션을 생성할 수 있으므로 런타임에서 Java 리플렉션에서 사용할 수 있습니다. 바이트코드에서 타입 어노테이션을 생성하려면 다음 단계를 따르십시오:

  1. 선언한 어노테이션이 적절한 어노테이션 타겟 (Java의 ElementType.TYPE_USE 또는 Kotlin의 AnnotationTarget.TYPE)과 보존 (AnnotationRetention.RUNTIME)을 가지고 있는지 확인하십시오.
  2. 어노테이션 클래스 선언을 JVM 바이트코드 타겟 버전 1.8+로 컴파일하십시오. -jvm-target=1.8 컴파일러 옵션으로 지정할 수 있습니다.
  3. 어노테이션을 사용하는 코드를 JVM 바이트코드 타겟 버전 1.8+ (-jvm-target=1.8)으로 컴파일하고 -Xemit-jvm-type-annotations 컴파일러 옵션을 추가하십시오.


현재 표준 라이브러리의 타입 어노테이션은 바이트코드에는 포함되지 않습니다. 이는 표준 라이브러리가 타겟 버전 1.6으로 컴파일되었기 때문입니다.

현재 기본적인 케이스만 지원됩니다:

  • 메서드 매개변수, 메서드 반환 타입 및 프로퍼티 타입에 대한 타입 어노테이션;
  • Smth<@Ann Foo>나 Array<@Ann Foo>와 같은 타입 인자의 불변 프로젝션.


다음 예에서는 String 타입에 대한 @Foo 어노테이션이 바이트코드에 포함될 수 있으며 라이브러리 코드에서 사용할 수 있습니다:

@Target(AnnotationTarget.TYPE)
annotation class Foo

class A {
fun foo(): @Foo String = "OK"
}

 

Kotlin/JS


JS 플랫폼에서는 Kotlin 1.4.0에서 다음과 같은 개선 사항이 제공됩니다:

  • 새로운 Gradle DSL
  • 새로운 JS IR 백엔드


새로운 Gradle DSL


kotlin.js Gradle 플러그인은 조정된 Gradle DSL을 제공하며, 여러 가지 새로운 구성 옵션을 제공하고 kotlin-multiplatform 플러그인에서 사용되는 DSL과 더 밀접하게 일치하도록 조정되었습니다. 가장 중요한 변경 사항 중 일부는 다음과 같습니다:

  • binaries.executable()를 통해 실행 가능한 파일을 생성하는 명시적 토글을 제공합니다. Kotlin/JS 실행 및 환경에 대한 자세한 내용은 여기에서 확인하십시오.
  • cssSupport를 통해 Gradle 구성에서 webpack의 CSS 및 스타일 로더를 구성할 수 있습니다. CSS 및 스타일 로더 사용에 대한 자세한 내용은 여기에서 확인하십시오.
  • npm 종속성의 개선된 관리를 제공하며, 버전 번호 또는 semver 버전 범위를 필수로 사용하고 devNpm, optionalNpm 및 peerNpm을 사용하여 개발, 피어 및 선택적 npm 종속성을 지원합니다. Gradle에서 직접 npm 패키지의 종속성 관리에 대한 자세한 내용은 여기에서 확인하십시오.
  • Kotlin 외부 선언 생성기인 Dukat에 대한 강화된 통합을 제공합니다. 외부 선언은 빌드 시간에 생성하거나 Gradle 작업을 통해 수동으로 생성할 수 있습니다.

 

새로운 JS IR 백엔드


Kotlin/JS를 위한 IR 백엔드는 현재 알파 안정성을 가지며, 데드 코드 제거를 통한 생성된 코드 크기 및 JavaScript 및 TypeScript와의 상호 운용성 개선을 포함한 Kotlin/JS 대상에 특화된 새로운 기능을 제공합니다.

Kotlin/JS IR 백엔드를 활성화하려면 gradle.properties에서 kotlin.js.compiler=ir 키를 설정하거나 Gradle 빌드 스크립트의 js 함수에 IR 컴파일러 유형을 전달하십시오.

 

kotlin {
    js(IR) { // 또는: LEGACY, BOTH
        // ...
    }
    binaries.executable()
}


새로운 백엔드를 구성하는 방법에 대한 자세한 내용은 Kotlin/JS IR 컴파일러 문서를 확인하십시오.

새로운 @JsExport 어노테이션과 Kotlin 코드에서 TypeScript 정의를 생성할 수 있는 능력을 통해 Kotlin/JS IR 컴파일러 백엔드는 JavaScript 및 TypeScript 상호 운용성을 개선합니다. 이로써 기존 도구와 통합하고 멀티플랫폼 프로젝트에서 코드 공유 기능을 활용하는 것이 더 쉬워집니다.

Kotlin/JS IR 컴파일러 백엔드에서 제공되는 기능에 대해 더 자세히 알아보십시오.

 

Kotlin/Native


1.4.0에서 Kotlin/Native는 다음과 같은 많은 새로운 기능과 개선 사항을 제공합니다:

  • Swift 및 Objective-C에서 suspending 함수 지원
  • Objective-C 제네릭 기본 지원
  • Objective-C/Swift 상호 운용에서 예외 처리
  • Apple 대상에 대한 기본적인 릴리스 .dSYMs 생성
  • 성능 향상
  • CocoaPods 종속성 간소화된 관리


Swift 및 Objective-C에서 Kotlin의 suspending 함수 지원


1.4.0에서는 Swift와 Objective-C에서 suspending 함수를 기본적으로 지원합니다. 이제 Kotlin 모듈을 Apple 프레임워크로 컴파일할 때 suspending 함수는 콜백 함수 (Swift/Objective-C 용어에서 completionHandler)를 가진 함수로 사용할 수 있습니다. 생성된 프레임워크의 헤더에 이러한 함수가 포함되어 있으면 Swift 또는 Objective-C 코드에서 호출하고 오버라이드할 수 있습니다.

예를 들어, 다음과 같은 Kotlin 함수를 작성하면:

 

suspend fun queryData(id: Int): String = ...


...Swift에서 다음과 같이 호출할 수 있습니다:

 

queryData(id: 17) { result, error in
   if let e = error {
       print("ERROR: \(e)")
   } else {
       print(result!)
   }
}


Swift와 Objective-C에서 suspending 함수 사용에 대한 자세한 내용은 여기에서 알아보세요.


Objective-C 제네릭 기본 지원


이전 버전의 Kotlin은 Objective-C 상호 운용에서 제네릭을 실험적으로 지원했습니다. 1.4.0부터 Kotlin/Native는 기본적으로 Kotlin 코드에서 제네릭을 생성하여 Apple 프레임워크를 생성합니다. 경우에 따라 기존의 Objective-C 또는 Swift 코드에서 Kotlin 프레임워크를 호출하는 것을 망칠 수 있습니다. 제네릭 없이 프레임워크 헤더를 작성하려면 -Xno-objc-generics 컴파일러 옵션을 추가하십시오.

 

kotlin {
    targets.withType<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget> {
        binaries.all {
            freeCompilerArgs += "-Xno-objc-generics"
        }
    }
}


Objective-C와의 상호 운용성 문서에 나열된 모든 구체적인 사항과 제한 사항이 여전히 유효하다는 점을 유의하십시오.


Objective-C/Swift 상호 운용에서 예외 처리


1.4.0에서는 Kotlin에서 생성한 Swift API의 예외 번역 방법과 관련하여 Swift API를 약간 변경했습니다. Kotlin과 Swift 간에 예외 처리에 대한 기본적인 차이점이 있습니다. Kotlin 예외는 모두 검사되지 않는 예외이며, Swift는 확인된 오류만 가지고 있습니다. 따라서 예상되는 예외를 Swift 코드에서 인식하려면 Kotlin 함수에 @Throws 어노테이션을 지정하여 잠재적인 예외 클래스 목록을 지정해야 합니다.

Swift 또는 Objective-C 프레임워크로 컴파일할 때 @Throws 어노테이션을 가진 함수는 Objective-C에서 NSError*를 생성하는 메서드로 나타내며, Swift에서는 throws 메서드로 나타납니다.

이전에 RuntimeException 및 Error 이외의 예외는 모두 NSError로 전파되었습니다. 이제 이 동작이 변경되어 @Throws 어노테이션의 매개변수로 지정된 클래스 (또는 그 서브클래스)의 인스턴스에 대해서만 NSError가 throw됩니다. Swift/Objective-C로 전달되는 다른 Kotlin 예외는 처리되지 않은 것으로 간주되며 프로그램이 종료됩니다.


Apple 대상에 대한 기본적인 릴리스 .dSYMs 생성


1.4.0부터 Kotlin/Native 컴파일러는 다린 플랫폼에서 릴리스 이진 파일에 대한 디버그 심볼 파일 (.dSYMs)을 기본적으로 생성합니다. 이 기능은 -Xadd-light-debug=disable 컴파일러 옵션으로 비활성화할 수 있습니다. 다른 플랫폼에서는 이 옵션이 기본적으로 비활성화되어 있습니다. Gradle에서 이 옵션을 전환하려면 다음을 사용하십시오:

 

kotlin {
    targets.withType<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget> {
        binaries.all {
            freeCompilerArgs += "-Xadd-light-debug={enable|disable}"
        }
    }
}


충돌 보고서 심볼화에 대한 자세한 내용은 여기에서 알아보세요.


성능 향상


Kotlin/Native는 개발 프로세스와 실행을 모두 가속화하는 다양한 성능 향상을 받았습니다. 여기에는 다음과 같은 예가 있습니다:

  • 객체 할당 속도를 향상하기 위해 미니멀록 메모리 할당기를 시스템 할당기 대안으로 제공합니다. 미니멀록은 일부 벤치마크에서 최대 2배 빠릅니다. 현재 Kotlin/Native에서 미니멀록 사용은 실험적입니다. -Xallocator=mimalloc 컴파일러 옵션을 사용하여 이를 전환할 수 있습니다.
  • C 상호 운용 라이브러리 구축 방식을 재작성했습니다. 새로운 도구를 사용하면 Kotlin/Native는 이전보다 최대 4배 빠르게 상호 운용 라이브러리를 생성하며 아티팩트는 이전보다 25%에서 30% 작습니다.
  • 전체 런타임 성능은 GC 최적화로 인해 향상되었습니다. 이 개선 사항은 특히 장기적으로 사용되는 객체가 많은 프로젝트에서 더욱 두드러집니다. HashMap 및 HashSet 컬렉션은 중복된 박싱을 피해 더 빨리 작동합니다.
  • 1.3.70에서 Kotlin/Native 컴파일 성능을 향상시키기 위해 프로젝트 종속성 캐싱 및 Gradle 데몬에서 컴파일러 실행을 위한 두 가지 새로운 기능을 소개했습니다. 이후 이러한 기능의 다양한 문제를 해결하고 전반적인 안정성을 향상시켰습니다.

 

CocoaPods 종속성 간소화된 관리


이전에 CocoaPods 의존성 관리자와 프로젝트를 통합한 후에는 멀티플랫폼 프로젝트의 일부인 iOS, macOS, watchOS 또는 tvOS 부분을 IntelliJ IDEA에서 분리하여 빌드할 수 없었습니다. 기타 부분은 IntelliJ IDEA에서 코드 하이라이팅 및 완성과 같은 코드 작업을 수행하면서 빌드할 수 있었습니다.

또한 CocoaPods (Pod 라이브러리)에 저장된 Objective-C 라이브러리에 대한 종속성을 추가할 때마다 IntelliJ IDEA에서 Xcode로 전환하여 pod install을 호출하고 Xcode에서 빌드해야 했습니다.

이제 IntelliJ IDEA에서 CocoaPods 종속성을 관리하고 코드 하이라이팅 및 완성과 같은 코드 작업을 할 수 있습니다. 또한 Xcode로 전환하지 않고 Gradle을 사용하여 Kotlin 프로젝트 전체를 빌드할 수 있습니다. 이것은 Swift/Objective-C 코드를 작성하거나 시뮬레이터 또는 장치에서 애플리케이션을 실행해야 할 때만 Xcode로 이동해야 함을 의미합니다.

이제 로컬로 저장된 Pod 라이브러리와 작업할 수도 있습니다.

필요에 따라 다음 사이에서 종속성을 추가할 수 있습니다:

  • Kotlin 프로젝트와 CocoaPods 저장소에 저장된 원격 Pod 라이브러리 또는 로컬 머신에 저장된 Pod 라이브러리 간
  • Kotlin Pod (CocoaPods 종속성으로 사용되는 Kotlin 프로젝트)과 하나 이상의 대상이 있는 Xcode 프로젝트 간


초기 구성을 완료하고 새로운 종속성을 cocoapods에 추가할 때 IntelliJ IDEA에서 프로젝트를 다시 가져옵니다. 새로운 종속성이 자동으로 추가됩니다. 추가 단계가 필요하지 않습니다.

종속성을 추가하는 방법에 대한 자세한 내용은 종속성 추가 방법을 참조하세요.

 

Kotlin Multiplatform


멀티플랫폼 프로젝트 지원은 Alpha 단계입니다. 나중에 호환성을 무시하고 변경되어 수동으로 마이그레이션을 필요로 할 수 있습니다. 피드백을 위해 YouTrack에서 받아들이고 있습니다.

Kotlin Multiplatform은 여러 플랫폼용으로 동일한 코드를 작성하고 유지하는 데 소요되는 시간을 줄이면서 네이티브 프로그래밍의 유연성과 이점을 유지합니다. 우리는 멀티플랫폼 기능 및 개선 사항에 노력을 계속 기울이고 있습니다:

  • 계층 구조 프로젝트 구조에서 여러 대상 간에 코드 공유
  • 계층 구조 내에서 네이티브 라이브러리 활용
  • kotlinx 종속성을 한 번만 지정


멀티플랫폼 프로젝트는 Gradle 6.0 이상이 필요합니다.


계층 구조 프로젝트 구조에서 여러 대상 간에 코드 공유


새로운 계층 구조 프로젝트 구조 지원으로 멀티플랫폼 프로젝트에서 여러 플랫폼 간에 코드를 공유할 수 있습니다.

이전에 멀티플랫폼 프로젝트에 추가된 코드는 하나의 대상에만 사용할 수 있고 다른 플랫폼에서 재사용할 수 없는 플랫폼별 소스 세트에 배치하거나 commonMain 또는 commonTest와 같은 공통 소스 세트에 배치할 수 있었습니다. 공통 소스 세트에서는 플랫폼별 API를 호출하기 위해 플랫폼별 actual 구현이 필요한 expect 선언을 사용하는 것이 가능했습니다.

이렇게하면 모든 플랫폼에서 코드를 공유하기 쉬웠지만, 특히 많은 공통 로직과 써드파티 API를 재사용할 수 있는 비슷한 플랫폼, 특히 유사한 플랫폼 간에만 공유하기는 그렇게 쉽지 않았습니다.

예를 들어, iOS를 대상으로 하는 전형적인 멀티플랫폼 프로젝트에서 iOS와 관련된 두 가지 iOS 대상이 있습니다. iOS ARM64 디바이스용과 x64 시뮬레이터용입니다. 이들은 별도의 플랫폼별 소스 세트를 가지고 있지만 실제로 디바이스와 시뮬레이터용으로 다른 코드가 필요한 경우는 드뭅니다. 또한 이들의 종속성은 많이 유사합니다. 따라서 iOS별 코드를 공유할 수 있습니다.

이런 설정에서 iOS 디바이스와 시뮬레이터 모두에서 공통으로 사용할 수 있는 Kotlin/Native 코드를 가진 두 가지 iOS 대상에 대한 공유 소스 세트를 가질 수 있으면 바람직할 것입니다. 

 

 

이제 Hierarchical 프로젝트 구조 지원을 사용하여 이 작업을 수행할 수 있으며, 이 지원은 어떤 대상에서 사용하는지에 따라 사용 가능한 API와 언어 기능을 추론하고 적응합니다.

 

일반적인 대상 조합에 대해서는 대상 바로가기를 사용하여 대상 간에 공유 소스 세트를 만들 수 있습니다.

예를 들어, 두 가지 iOS 대상 및 위에 표시된 공유 소스 세트를 ios() 바로가기로 만들 수 있습니다.

 

kotlin {
    ios() // iOS 디바이스 및 시뮬레이터 대상; iosMain 및 iosTest 소스 세트
}


다른 대상 조합의 경우에는 sourceSets를 dependsOn 관계로 수동으로 연결하여 계층 구조를 수동으로 만들 수 있습니다.

 

 

kotlin{
    sourceSets {
        val desktopMain by creating {
            dependsOn(commonMain)
        }
        val linuxX64Main by getting {
            dependsOn(desktopMain)
        }
        val mingwX64Main by getting {
            dependsOn(desktopMain)
        }
        val macosX64Main by getting {
            dependsOn(desktopMain)
        }
    }
}


계층 구조 프로젝트 구조 덕분에 라이브러리도 특정 대상 하위 집합을 위해 공통 API를 제공할 수 있습니다. 코드 공유에 대한 자세한 내용은 코드를 공유하는 방법을 알아보세요.


계층 구조 내에서 네이티브 라이브러리 활용


여러 네이티브 대상 간에 공유된 소스 세트에서 Foundation, UIKit 및 POSIX와 같은 플랫폼 종속 라이브러리를 사용할 수 있습니다. 이렇게 하면 플랫폼별 종속성으로 제한받지 않고 더 많은 네이티브 코드를 공유할 수 있습니다.

추가적인 단계는 필요하지 않습니다. IntelliJ IDEA는

 공유 코드에서 사용할 수 있는 공통 선언을 감지하는 데 도움을 줄 것입니다.

플랫폼 종속 라이브러리 사용에 대한 자세한 내용은 플랫폼 종속 라이브러리 사용 방법을 참조하세요.


의존성을 한 번만 지정


이제 동일한 라이브러리의 다른 변형에 대한 의존성을 공유 및 플랫폼별 소스 세트에 지정하는 대신 공유 소스 세트에서 의존성을 한 번만 지정해야 합니다.

 

kotlin {
    sourceSets {
        val commonMain by getting {
            dependencies {
                implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1")
            }
        }
    }
}


더 이상 플랫폼을 지정하는 접미사가 있는 kotlinx 라이브러리 아티팩트 이름을 사용하지 마세요. 이 예제에서는 kotlinx-coroutines-core입니다.

그러나 이 변경 사항은 현재 다음에는 영향을 주지 않습니다:

  • stdlib 라이브러리 - Kotlin 1.4.0부터 stdlib 종속성이 자동으로 추가됩니다.
  • kotlin.test 라이브러리 - 여전히 test-common 및 test-annotations-common을 사용해야 합니다. 이러한 종속성은 나중에 처리될 예정입니다.


특정 플랫폼에만 종속성이 필요한 경우, -jvm 또는 -js와 같은 접미사로 표시된 표준 및 kotlinx 라이브러리의 플랫폼별 변형을 사용할 수 있습니다. 예를 들어 kotlinx-coroutines-core-jvm을 사용할 수 있습니다.

의존성 구성에 대한 자세한 내용은 의존성 구성 방법을 참조하세요.

 

Gradle 프로젝트 개선사항


Kotlin Multiplatform, Kotlin/JVM, Kotlin/Native 및 Kotlin/JS에 특정한 Gradle 프로젝트 기능 및 개선점 외에도 모든 Kotlin Gradle 프로젝트에 적용되는 몇 가지 변경 사항이 있습니다:

  • 표준 라이브러리에 대한 의존성이 이제 기본적으로 추가됩니다.
  • Kotlin 프로젝트는 최신 버전의 Gradle이 필요합니다.
  • IDE에서 Kotlin Gradle DSL의 지원이 향상되었습니다.


표준 라이브러리에 대한 의존성이 기본적으로 추가됨


이제 Kotlin Gradle 프로젝트, 멀티플랫폼 프로젝트를 포함한 모든 Kotlin Gradle 프로젝트에서 stdlib 라이브러리에 대한 의존성을 선언할 필요가 없습니다. 이 의존성은 기본적으로 추가됩니다.

자동으로 추가된 표준 라이브러리는 Kotlin Gradle 플러그인과 동일한 버전이 될 것이므로 동일한 버전의 Kotlin Gradle 플러그인을 사용하고 있는지 확인하십시오.

플랫폼별 소스 세트의 경우 해당 플랫폼별 라이브러리의 변형을 사용하고 나머지 부분에는 공통 표준 라이브러리가 추가됩니다. Kotlin Gradle 플러그인은 Gradle 빌드 스크립트의 kotlinOptions.jvmTarget 컴파일러 옵션에 따라 적절한 JVM 표준 라이브러리를 선택할 것입니다.

기본 동작을 변경하는 방법을 알아보세요.


Kotlin 프로젝트의 최소 Gradle 버전


Kotlin 프로젝트에서 새로운 기능을 즐기려면 Gradle을 최신 버전으로 업데이트하십시오. 멀티플랫폼 프로젝트는 Gradle 6.0 이상이 필요하며, 다른 Kotlin 프로젝트는 Gradle 5.4 이상에서 작동합니다.

 

IDE에서 *.gradle.kts 지원 향상


1.4.0 버전에서는 Gradle Kotlin DSL 스크립트 (*.gradle.kts 파일)의 IDE 지원을 계속 향상시켰습니다. 새로운 버전에서 제공되는 내용은 다음과 같습니다:

- 성능 향상을 위한 스크립트 구성 명시적 로딩. 이전에는 빌드 스크립트를 변경하면 백그라운드에서 자동으로 변경 내용이 로드되었습니다. 성능을 향상하기 위해 1.4.0에서는 빌드 스크립트 구성의 자동 로딩을 비활성화했습니다. 이제 IDE는 변경 사항을 명시적으로 적용할 때만 변경 사항을 로드합니다.

Gradle 6.0 이전 버전에서는 편집기에서 Load Configuration를 클릭하여 스크립트 구성을 수동으로 로드해야 합니다.

 


Gradle 6.0 이상에서는 Gradle 변경 내용을 명시적으로 적용하려면 Load Gradle Changes를 클릭하거나 Gradle 프로젝트를 다시 가져 오면 됩니다.

Gradle 6.0 이상과 IntelliJ IDEA 2020.1에서는 스크립트 구성을 로드하는 데 걸리는 전체 프로젝트를 업데이트하는 것보다 훨씬 덜 시간이 걸리는 Load Script Configurations라는 추가 작업을 추가했습니다.

 


IntelliJ IDEA 2020.1 이상과 Gradle 6.0 이상을 사용하면 새로 생성한 스크립트나 Kotlin 플러그인을 처음으로 사용하는 프로젝트를 열 때 이전에 Load Script Configurations를 수행해야 합니다.

Gradle 6.0 이상에서는 이전에 스크립트를 개별적으로 로드했던 이전 구현과 달리 모든 스크립트를 한 번에 로드할 수 있습니다. 각 요청이 Gradle 구성 단계를 실행해야 하므로 대규모 Gradle 프로젝트에 대해 리소스 집약적일 수 있습니다.

현재 이러한 로드는 build.gradle.kts 및 settings.gradle.kts 파일로 제한됩니다 (관련된 이슈에 투표하세요). init.gradle.kts 또는 적용된 스크립트 플러그인에 대한 하이라이팅을 사용하려면 이전 메커니즘을 사용하여 이러한 스크립트를 독립형 스크립트에 추가하십시오. 해당 스크립트에 대한 구성은 필요할 때 별도로 로드됩니다. 이러한 스크립트에 대한 자동 리로드도 활성화할 수 있습니다.

 


- 더 나은 오류 보고. 이전에는 Gradle 데몬에서 오류에 관한 정보만 별도의 로그 파일에서 볼 수 있었습니다. 이제 Gradle 데몬은 오류에 관한 모든 정보를 직접 반환하고 Build 도구 창에 표시합니다. 이로 인해 시간과 노력을 절약할 수 있습니다.

 

표준 라이브러리


1.4.0 버전에서 코틀린 표준 라이브러리의 가장 중요한 변경 사항 목록입니다:

  • 공통 예외 처리 API
  • 배열 및 컬렉션을 위한 새로운 함수
  • 문자열 조작 함수
  • 비트 연산
  • 위임된 프로퍼티 개선 사항
  • KType에서 Java Type으로 변환
  • Kotlin 리플렉션을 위한 Proguard 구성
  • 기존 API 개선
  • stdlib 아티팩트를 위한 module-info 디스크립터
  • 폐기 사항
  • 폐기된 실험적 코루틴 제외


공통 예외 처리 API


다음 API 요소가 공통 라이브러리로 이동되었습니다:

 

  • Throwable.stackTraceToString() 확장 함수는 throwable의 세부 정보 및 스택 트레이스를 반환하며, Throwable.printStackTrace() 함수는 이 설명을 표준 오류 출력에 인쇄합니다.
  • Throwable.addSuppressed() 함수는 예외를 전달하기 위해 억제된 예외를 지정할 수 있으며, Throwable.suppressedExceptions 속성은 모든 억제된 예외의 목록을 반환합니다.
  • @Throws 어노테이션은 함수가 플랫폼 메서드( JVM 또는 네이티브 플랫폼)로 컴파일될 때 확인될 예외 유형을 나열합니다.

 

배열 및 컬렉션을 위한 새로운 함수

 

컬렉션


1.4.0 버전에서 표준 라이브러리에는 컬렉션 작업을 위한 유용한 함수가 여럿 포함되어 있습니다:

- setOfNotNull(): 주어진 인수 중에서 null이 아닌 항목으로 이루어진 집합을 만듭니다.

 

val set = setOfNotNull(null, 1, 2, 0, null)
println(set)


sequences에 대한 shuffled() 함수

val numbers = (0 until 50).asSequence()
val result = numbers.map { it * 2 }.shuffled().take(5)
println(result.toList()) // 100 미만의 5개의 무작위 짝수

 

- onEach() 및 flatMap()에 대한 Indexed() 대응 함수. 이 함수들은 컬렉션 요소에 대해 요소 인덱스를 매개 변수로 적용하는 작업을 수행합니다.

listOf("a", "b", "c", "d").onEachIndexed {
    index, item -> println(index.toString() + ":" + item)
}

val list = listOf("hello", "kot", "lin", "world")
val kotlin = list.flatMapIndexed { index, item ->
    if (index in 1..2) item.toList() else emptyList() 
}


- *OrNull() 함수들: randomOrNull()reduceOrNull() 및 reduceIndexedOrNull(). 이 함수들은 빈 컬렉션에 대해 null을 반환합니다.

val empty = emptyList<Int>()
empty.reduceOrNull { a, b -> a + b }
//empty.reduce { a, b -> a + b } // 예외: 빈 컬렉션은 축소될 수 없습니다.


- runningFold(), scan(), 및 runningReduce(): 이들 함수는 주어진 작업을 순차적으로 컬렉션 요소에 적용하며, fold() 및 reduce()와 유사하게 작동하지만 이러한 새로운 함수들은 중간 결과의 전체 시퀀스를 반환합니다.

val numbers = mutableListOf(0, 1, 2, 3, 4, 5)
val runningReduceSum = numbers.runningReduce { sum, item -> sum + item }
val runningFoldSum = numbers.runningFold(10) { sum, item -> sum + item }


- sumOf(): 선택자 함수를 사용하고 컬렉션의 모든 요소에 대한 값을 반환합니다. sumOf()는 Int, Long, Double, UInt 및 ULong 유형의 합계를 생성할 수 있습니다. JVM에서는 BigInteger 및 BigDecimal도 사용할 수 있습니다.

val order = listOf<OrderItem>(
    OrderItem("Cake", price = 10.0, count = 1),
    OrderItem("Coffee", price = 2.5, count = 3),
    OrderItem("Tea", price = 1.5, count = 2))

val total = order.sumOf { it.price * it.count } // Double
val count = order.sumOf { it.count } // Int


- min() 및 max() 함수는 Kotlin 컬렉션 API에서 사용되는 네이밍 컨벤션을 준수하기 위해 minOrNull() 및 maxOrNull()로 이름이 변경되었습니다. 함수 이름에 *OrNull 접미사가 있는 경우 수신 컬렉션이 비어있으면 null을 반환한다는 의미입니다. 동일한 원리가 minBy(), maxBy(), minWith(), maxWith()에도 적용됩니다.

- 새로운 minOf() 및 maxOf() 확장 함수도 추가되었습니다. 이 함수들은 컬렉션 항목에서 주어진 선택자 함수의 최소값과 최대값을 반환합니다.

val order = listOf<OrderItem>(
    OrderItem("Cake", price = 10.0, count = 1),
    OrderItem("Coffee", price = 2.5, count = 3),
    OrderItem("Tea", price = 1.5, count = 2))
val highestPrice = order.maxOf { it.price }

 

 

minOfWith() 및 maxOfWith()도 있으며 이 함수들은 Comparator를 인수로 사용하며 빈 컬렉션에 대해 null을 반환하는 *OrNull() 버전도 있습니다.

- 배열 및 컬렉션 간의 변환을 위한 새로운 flatMapflatMapTo 오버로드도 추가되어 변환 결과 유형이 수신자 유

  • 반복 가능한 자료형(Iterable), 배열(Array), 그리고 맵(Map)에 대한 변환
  • 시퀀스(Sequence)에 대한 반복 가능한 자료형으로의 변환

 

val list = listOf("kot", "lin")
val lettersList = list.flatMap { it.asSequence() }
val lettersSeq = list.asSequence().flatMap { it.toList() }


- 가변 리스트에서 요소를 제거하기 위한 removeFirst()removeLast() 단축키, 그리고 이러한 함수의 *orNull() 대응 함수.


배열


다양한 컨테이너 유형을 사용할 때 일관된 경험을 제공하기 위해 배열에 대한 새로운 함수도 추가되었습니다:

- shuffle(): 배열 요소를 무작위로 섞습니다.

- onEach(): 주어진 작업을 각 배열 요소에 수행하고 배열 자체를 반환합니다.

- associateWith() 및 associateWithTo(): 배열 요소를 키로 사용하여 맵을 작성합니다.

- 배열 부분 범위에 대한 reverse(): 부분 범위의 요소 순서를 뒤집습니다.

- 배열 부분 범위에 대한 sortDescending(): 부분 범위의 요소를 내림차순으로 정렬합니다.

- 배열 부분 범위에 대한 sort() 및 sortWith(): 이제 공통 라이브러리에서 사용할 수 있습니다.

 

var language = ""
val letters = arrayOf("k", "o", "t", "l", "i", "n")
val fileExt = letters.onEach { language += it }
    .filterNot { it in "aeuio" }.take(2)
    .joinToString(prefix = ".", separator = "")
println(language) // "kotlin"
println(fileExt) // ".kt"

letters.shuffle()
letters.reverse(0, 3)
letters.sortDescending(2, 5)
println(letters.contentToString()) // [k, o, t, l, i, n]


또한, CharArray/ByteArray와 String 간의 변환을 위한 새로운 함수가 있습니다:

- ByteArray.decodeToString() 및 String.encodeToByteArray()


- CharArray.concatToString() 및 String.toCharArray()

val str = "kotlin"
val array = str.toCharArray()
println(array.concatToString())


ArrayDeque


ArrayDeque 클래스가 추가되었습니다. 이 클래스는 더블 엔디드 큐(double-ended queue)의 구현입니다. 더블 엔디드 큐는 큐나 스택이 필요한 경우 코드에서 시작 또는 끝에서 요소를 추가하거나 제거할 수 있도록 해주며 상기된 작업은 상환 상수 시간에 수행됩니다.

fun main() {
    val deque = ArrayDeque(listOf(1, 2, 3))
    
    deque.addFirst(0)
    deque.addLast(4)
    println(deque) // [0, 1, 2, 3, 4]
    
    println(deque.first()) // 0
    println(deque.last()) // 4
    
    deque.removeFirst()
    deque.removeLast()
    println(deque) // [1, 2, 3]
}


ArrayDeque 구현은 크기 조정 가능한 배열을 사용합니다. 이 배열은 원형 버퍼, 배열 내에 내용을 저장하며 이 배열은 가득 찼을 때만 크기를 조정합니다.


문자열 조작 함수


1.4.0 버전의 표준 라이브러리에는 문자열 조작을 위한 API의 여러 개선 사항이 포함되어 있습니다:

- StringBuilder에는 set(), setRange(), deleteAt(), deleteRange(), appendRange() 및 기타 유용한 새로운 확장 함수가 추가되었습니다.

val sb = StringBuilder("Bye Kotlin 1.3.72")
sb.deleteRange(0, 3)
sb.insertRange(0, "Hello", 0 ,5)
sb.set(15, '4')
sb.setRange(17, 19, "0")
print(sb.toString())


- 일부 기존의 StringBuilder 함수들이 공통 라이브러리에서 사용할 수 있습니다. 그 중에는 append(), insert(), substring(), setLength() 등이 포함됩니다.

- 공통 라이브러리에는 새로운 함수 Appendable.appendLine() 및 StringBuilder.appendLine()도 추가되었습니다. 이들 함수는 해당 클래스의 JVM 전용 appendln() 함수를 대체합니다.

 

println(buildString {
    appendLine("Hello,")
    appendLine("world")
})

 

비트 연산


비트 조작을 위한 새로운 함수들도 추가되었습니다:

 

  • countOneBits()
  • countLeadingZeroBits()
  • countTrailingZeroBits()
  • takeHighestOneBit()
  • takeLowestOneBit()
  • rotateLeft() 및 rotateRight() (실험적)

 

val number = "1010000".toInt(radix = 2)
println(number.countOneBits())
println(number.countTrailingZeroBits())
println(number.takeHighestOneBit().toString(2))


Delegated properties 개선 사항


1.4.0 버전에서는 위임된 프로퍼티를 사용하는 경험을 개선하기 위한 새로운 기능들이 추가되었습니다:

 

  • 이제 프로퍼티를 다른 프로퍼티에 위임할 수 있습니다.
  • 새로운 인터페이스 PropertyDelegateProvider는 하나의 선언으로 위임 공급자를 생성하는 데 도움을 줍니다.
  • ReadWriteProperty는 이제 ReadOnlyProperty를 확장하므로 읽기 전용 프로퍼티에 대해 둘 다 사용할 수 있습니다.


또한 결과 바이트 코드 크기를 줄이는 몇 가지 최적화도 수행되었습니다. 이러한 최적화에 대한 자세한 내용은 블로그 포스트에서 확인할 수 있습니다.


KType에서 Java Type으로 변환


stdlib에는 코틀린 타입에서 전체 kotlin-reflect 종속성을 사용하지 않고도 java.lang.reflect.Type을 얻을 수 있는 새로운 확장 프로퍼티 KType.javaType(현재 실험 중)가 포함되어 있습니다.

import kotlin.reflect.javaType
import kotlin.reflect.typeOf

@OptIn(ExperimentalStdlibApi::class)
inline fun <reified T> accessReifiedTypeArg() {
   val kType = typeOf<T>()
   println("Kotlin type: $kType")
   println("Java type: ${kType.javaType}")
}

@OptIn(ExperimentalStdlibApi::class)
fun main() {
   accessReifiedTypeArg<String>()
   // Kotlin type: kotlin.String
   // Java type: class java.lang.String
  
   accessReifiedTypeArg<List<String>>()
   // Kotlin type: kotlin.collections.List<kotlin.String>
   // Java type: java.util.List<java.lang.String>
}


Kotlin 리플렉션을 위한 Proguard 구성


1.4.0부터는 kotlin-reflect.jar에 Kotlin Reflection용 Proguard/R8 구성이 내장되어 있습니다. 이로 인해 R8 또는 Proguard를 사용하는 대부분의 Android 프로젝트가 추가 구성없이도 kotlin-reflect를 사용할 수 있습니다. 이제 kotlin-reflect

 내부의 Proguard 규칙을 복사하여 추가 구성이 필요하지 않습니다. 다만 해당 API를 반영할 모든 API를 명시적으로 나열해야 합니다.


기존 API 개선

 

  • 몇몇 함수들은 이제 null 수신자에서도 작동합니다. 예를 들어:
  • 문자열의 toBoolean()
  • 배열의 contentEquals(), contentHashcode(), contentToString()
    Double 및 Float의 NaN, NEGATIVE_INFINITY 및 POSITIVE_INFINITY는 이제 const로 정의되어 있으므로 주석 인수로 사용할 수 있습니다.
  • Double 및 Float의 크기에 대한 새로운 상수 SIZE_BITS 및 SIZE_BYTES가 추가되었습니다.
  • maxOf() 및 minOf() 최상위 함수는 가변 인수(vararg)를 받을 수 있습니다.

 

stdlib 아티팩트를 위한 module-info 디스크립터


Kotlin 1.4.0에서는 기본 표준 라이브러리 아티팩트에 module-info.java 모듈 정보를 추가했습니다. 이렇게 하면 jlink 도구를 사용하여 앱에 필요한 플랫폼 모듈만 포함된 사용자 지정 Java 런타임 이미지를 생성하는 데 이들을 사용할 수 있습니다. 이전에도 Kotlin 표준 라이브러리 아티팩트를 jlink와 함께 사용할 수 있었지만 "modular" 분류를 가진 별도의 아티팩트를 사용해야 했으며 전체 설정이 직관적이지 않았습니다. Android에서는 module-info가 포함된 jar 파일을 올바르게 처리할 수 있도록 Android Gradle 플러그인 버전 3.2 이상을 사용해야 합니다.


폐기 사항

 

Double 및 Float의 toShort() 및 toByte() 함수


좁은 값 범위와 작은 변수 크기로 인해 예상치 못한 결과가 발생할 수 있으므로 이러한 함수들을 폐기했습니다. 부동 소수점 수를 Byte 또는 Short로 변환하려면 두 단계의 변환을 사용하십시오. 먼저 Int로 변환한 다음 다시 대상 유형으로 변환하십시오.

부동 소수점 배열에 대한 contains(), indexOf(), 및 lastIndexOf() 확장 함수들

 

IEEE 754 표준 동등성을 사용하여 빈도수를 계산하므로 일부 모호한 경우에는 전체 순서 동등성과 충돌합니다. 자세한 내용은 해당 이슈를 참조하십시오.

컬렉션 함수인 min() 및 max()

 

이러한 함수 대신 빈 컬렉션에 대해 null을 반환하는 minOrNull() 및 maxOrNull()을 사용하도록 권장합니다. 자세한 내용은 해당 이슈를 참조하십시오.

실험적 코루틴의 폐기 제외

 

폐기된 실험적 코루틴: kotlin.coroutines.experimental API는 1.3.0 버전에서 kotlin.coroutines로 폐기되었습니다. 1.4.0 버전에서는 표준 라이브러리에서 제거되었습니다. JVM에서 아직 사용 중인 사용자를 위해 실험적 코루틴 API 모두를 제공하는 kotlin-coroutines-experimental-compat.jar 호환 아티팩트를 제공합니다. 이것은 Maven에 게시되었으며 표준 라이브러리와 함께 Kotlin 배포에 포함되어 있습니다.


안정적인 JSON 직렬화


Kotlin 1.4.0에서는 kotlinx.serialization의 첫 번째 안정적인 버전인 1.0.0-RC를 제공합니다. 이제 kotlinx-serialization-core(이전에 kotlinx-serialization-runtime으로 알려진)에서 JSON 직렬화 API를 안정적으로 선언합니다. 다른 직렬화 형식에 대한 라이브러리는 여전히 실험적이며 핵심 라이브러리의 일부 고급 부분도 그렇습니다.

JSON 직렬화 API를 더 일관되고 사용하기 쉽게 만들기 위해 API를 크게 개선했습니다. 이제부터는 JSON 직렬화 API를 하위 호환 방식으로 계속 개발할 것입니다. 그러나 이전 버전을 사용한 경우, 1.0.0-RC로 마이그레이션할 때 코드 일부를 다시 작성해야 할 것입니다. 이를 돕기 위해 Kotlin Serialization Guide를 제공합니다. 이 문서는 kotlinx.serialization의 가장 중요한 기능을 사용하는 과정을 안내하며 마주칠 수 있는 문제를 해결하는 데 도움이 될 것입니다.

참고: kotlinx-serialization 1.0.0-RC는 Kotlin 컴파일러 1.4와만 호환됩니다. 이전 컴파일러 버전과는 호환되지 않습니다.

 

스크립팅과 REPL

 

1.4.0에서 Kotlin 스크립팅은 기능 및 성능 개선과 다른 업데이트를 통해 혜택을 봅니다. 주요 변경 사항은 다음과 같습니다:

  • 새로운 종속성 해결 API
  • 새로운 REPL API
  • 컴파일된 스크립트 캐시
  • 아티팩트 이름 변경

 

Kotlin 스크립팅에 더 익숙해지도록 하기 위해 예제가 포함된 프로젝트를 준비했습니다. 이 프로젝트에는 표준 스크립트(*.main.kts) 및 Kotlin 스크립팅 API 및 사용자 정의 스크립트 정의의 사용 예제가 포함되어 있습니다. 한 번 시도하고 문제 추적기를 사용하여 피드백을 공유해 주세요.


새로운 종속성 해결 API


1.4.0에서는 외부 종속성(Maven 아티팩트와 같은)을 해결하기 위한 새로운 API를 도입했으며 이에 대한 구현도 함께 제공합니다. 이 API는 kotlin-scripting-dependencies 및 kotlin-scripting-dependencies-maven이라는 새로운 아티팩트에 게시됩니다. 이전의 종속성 해결 기능인 kotlin-script-util 라이브러리는 이제 폐기되었습니다.


새로운 REPL API


새로운 실험적 REPL API는 이제 Kotlin 스크립팅 API의 일부입니다. 이에 대한 여러 구현도 게시된 아티팩트에 포함되어 있으며 일부는 코드 완성과 같은 고급 기능을 갖고 있습니다. 이 API는 Kotlin Jupyter 커널에서 사용하며 이제 자신의 사용자 정의 셸과 REPL에서 시도할 수 있습니다.


컴파일된 스크립트 캐시


Kotlin 스크립팅 API는 이제 컴파일된 스크립트 캐시를 구현할 수 있는 기능을 제공하여 변경되지 않은 스크립트의 후속 실행을 크게 가속화할 수 있습니다. 기본 고급 스크립트 구현인 kotlin-main-kts에 이미 자체 캐시가 있습니다.


아티팩트 이름 변경


아티팩트 이름에 대한 혼란을 피하기 위해 kotlin-scripting-jsr223-embeddable 및 kotlin-scripting-jvm-host-embeddable을 간단히 kotlin-scripting-jsr223 및 kotlin-scripting-jvm-host로 이름을 변경했습니다. 이러한 아티팩트는 kotlin-compiler-embeddable 아티팩트에 의존하며 충돌 사용을 피하기 위해 번들된 서드파티 라이브러리를 무색으로 만듭니다. 이 이름 변경으로 인해 일반적으로 더 안전한 kotlin-compiler-embeddable의 사용이 스크립팅 아티팩트의 기본값이 되었습니다. 어떤 이유로든 무색된 kotlin-compiler에 의존하는 아티팩트가 필요한 경우 -unshaded 접미사가 있는 아티팩트 버전을 사용하면 됩니다. 이러한 이름 변경은 직접 사용되어야 하는 스크립팅 아티팩트에만 영향을 미치며 다른 아티팩트의 이름은 변경되지 않습니다.

 

Kotlin 1.4.0로의 마이그레이션


Kotlin 플러그인의 마이그레이션 도구를 사용하면 이전 버전의 Kotlin에서 1.4.0으로 프로젝트를 마이그레이션하는 데 도움을 받을 수 있습니다.

단순히 Kotlin 버전을 1.4.0으로 변경하고 Gradle 또는 Maven 프로젝트를 다시 가져오세요. 그런 다음 IDE는 마이그레이션에 대해 물어볼 것입니다.

동의하면 코드 검사를 실행하여 코드를 확인하고 1.4.0에서 작동하지 않거나 권장되지 않는 부분에 대한 수정 제안을 제공할 것입니다.

 


코드 검사에는 다른 심각도 수준이 있으며, 어떤 제안을 받아들일지 무시할지 결정하는 데 도움이 됩니다.

 


Kotlin 1.4.0은 기능 릴리스이며 따라서 언어에 호환되지 않는 변경 사항을 가져올 수 있습니다. Kotlin 1.4의 호환성 가이드에서 이러한 변경 사항의 자세한 목록을 찾을 수 있습니다.

 

원문

 

https://kotlinlang.org/docs/whatsnew14.html#migrating-to-kotlin-1-4-0

반응형

댓글