본문 바로가기
Kotlin/Release Notes

[Kotlin Release Notes] Kotlin 1.3.50 released

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

2019년 8월 22일

 

Kotlin 1.3.50이 오늘 출시되었음을 기쁘게 알려드립니다. 이번 버전의 주요 초점은 다음과 같습니다.

  • 새로운 Duration 및 Time Measurement API 설계 (미리보기로 제공).
  • 향상된 Java-to-Kotlin 컨버터 작업.
  • Gradle Kotlin/JS 프로젝트에서 npm 종속성을 위한 외부 선언 실험 생성 (Dukat 사용).
  • IntelliJ IDEA Ultimate에서 Kotlin/Native 코드를 디버깅하기 위한 별도의 플러그인.
  • 멀티플랫폼 프로젝트에서 Java 컴파일 지원.

 

변경 사항의 완전한 목록은 변경 로그에서 확인할 수 있습니다. 항상 외부 기여자분들께 감사드립니다. 이제 자세히 살펴보겠습니다!


Kotlin 1.4를 위한 Null-check 최적화 계획


아마도 알고 계실 것처럼 Kotlin은 널 포인터 예외 (NullPointerException)의 가능성을 줄이기 위해 널 가능성을 지원합니다. 그러나 Java 코드와의 상호 운용성 때문에 완전히 NPE를 피하는 것은 불가능합니다. 개발자가 NPE의 원인을 더 잘 이해할 수 있도록 도와주기 위해 Kotlin 컴파일러는 순수한 NPE 대신 명확한 오류 메시지를 가진 다양한 종류의 런타임 예외를 던집니다. 그러나 이 접근 방식에는 단점이 있었습니다. Kotlin 컴파일러 또는 Android R8 옵티마이저와 같은 바이트코드 처리 도구가 수행할 수 있는 널 확인 최적화의 가능성을 줄입니다.

이 문제를 해결하기 위해 Kotlin 1.4부터 모든 런타임 널 확인은 KotlinNullPointerException, IllegalStateException, IllegalArgumentException 및 TypeCastException 대신 java.lang.NullPointerException을 던질 것입니다. 이는 !! 연산자, 메서드 전체 중에 있는 매개변수 널 확인, 플랫폼 타입 표현식 널 확인 및 비 널 타입을 가진 as 연산자에 해당합니다. 이는 lateinit 널 확인 및 checkNotNull 또는 requireNotNull과 같은 명시적 라이브러리 함수 호출에는 적용되지 않습니다.

개발자 관점에서는 많이 변경되지 않을 것임에 유의하십시오. Kotlin 코드는 여전히 이전과 동일한 오류 메시지를 가진 예외를 던질 것입니다. 예외의 유형은 변경되지만 전달되는 정보는 동일합니다. 예를 들어, 다음 코드는 현재 "JavaCode.getNull() must not be null"이라는 오류 메시지와 함께 IllegalStateException을 던집니다.

 

fun main() {
    duplicate(JavaCode.getNull())  // 1
}

fun duplicate(s: String) = s + s

public class JavaCode {
    public static String getNull() { return null; }
}

 

1번 라인에서 duplicate 함수 호출 바로 전에는 JavaCode.getNull() 표현식이 null을 저장하면 이 예외를 던지는 특수 확인이 생성됩니다. Kotlin 1.4부터 이 코드는 여전히 "JavaCode.getNull() must not be null"이라는 동일한 메시지를 가진 NullPointerException을 던질 것입니다.

이러한 동작 변경 이후에는 최적화 도구들이 가능한 경우 중복되는 널 확인을 제거하여 바이트코드에 있는 전체 널 확인 수를 줄일 수 있게 될 것입니다. 모든 확인이 동일한 NPE 예외를 던지기 때문에 한 번만 남을 수 있습니다. 이러한 최적화는 아직 해당 도구에서 구현되어야 하며 구현되면 더 많은 세부 정보가 제공될 것입니다. 그러나 예외 유형의 변경은 미래에 이러한 최적화를 가능하게 합니다.

 

표준 라이브러리 변경 사항


새로운 함수는 모두 "실험적" 상태로 표준 라이브러리에 추가됩니다.


Duration and Time Measurement API


미리보기로 사용 가능한 새로운 기간 및 시간 측정 API가 있습니다. Duration은 초, 밀리초, 나노초 등 다양한 단위로 측정할 수 있습니다. 서로 다른 단위 사이의 혼란은 버그의 잘 알려진 원인입니다. API가 Long과 같은 원시 값으로 저장된 기간을 예상한다면 값을 잘못된 단위로 전달할 수 있으며, 불행하게도 형식 시스템은 이를 방지하는 데 도움을 주지 않습니다. 기간을 저장하는 정규 클래스를 만들면이 문제는 해결되지만 다른 문제가 발생합니다. 추가 할당이 발생합니다.

인라인 클래스는 이에 대한 매우 우아한 해결책을 제공합니다. 인라인 클래스는 유형 시스템 보증과 할당 없는 접근을 모두 제공합니다. 이제 API는 Duration 유형을 사용하고 모든 클라이언트는 원하는 단위로 시간을 명시적으로 지정해야 합니다. Duration은 인라인 클래스로 선언되었으므로 내부에서 추가 할당이 발생하지 않습니다:

 

import kotlinx.coroutines.delay
import kotlin.time.*

@ExperimentalTime
suspend fun greetAfterTimeout(duration: Duration) {
    delay(duration.toLongMilliseconds())
    println("Hi!")
}

@UseExperimental(ExperimentalTime::class)
suspend fun main() {
    greetAfterTimeout(100.milliseconds)
    greetAfterTimeout(1.seconds)
}

 

MonoClock 지원


이 릴리스에서는 monotonic clock을 나타내는 MonoClock을 지원합니다. 프로그램에서 특정 시점부터 시간 기간을 측정하는 권장 방법은 시스템 시간에 의존하지 않는 monotonic clock을 사용하는 것입니다. 시스템 시간은 외부에서 변경될 수 있으며 이로 인해 잘못된 동작이 발생할 수 있습니다. monotonic clock은 주어진 시간 지점 간의 시간 차이만 측정할 수 있지만 "현재 시간"을 알지 못합니다.

Clock 인터페이스는 시간 간격을 측정하기 위한 일반적인 API를 제공합니다. MonoClock은 Clock을 구현하는 객체으로서 다른 플랫폼에서 모노토닉 시간의 기본 소스를 제공합니다.

Clock 인터페이스를 사용할 때 작업 시작 시간을 명시적으로 표시하고 나중에 시작 지점에서 경과한 시간을 표시합니다. 이것은 특히 시간을 측정하는 시작과 끝을 다른 함수에서 시작하고 끝내고 싶을 때 편리합니다:

 

import kotlin.time.*

@UseExperimental(ExperimentalTime::class)
fun main() {
    val clock = MonoClock
    val mark = clock.markNow() // 첫 번째 함수 내부에 있을 수 있음
    Thread.sleep(10)           // 작업
    println(mark.elapsedNow()) // 두 번째 함수 내부에 있을 수 있음
}

 

measureTimedValue 함수


measureTimedValue 함수를 사용하면 주어진 작업의 지속 시간을 측정하고 결과와 경과된 시간 간격의 지속 시간을 함께 얻을 수 있습니다. 이 함수는 MonoClock을 사용하여 경과된 시간을 측정합니다.

 

import kotlin.time.*

@UseExperimental(ExperimentalTime::class)
fun main() {
    val (value, duration) = measureTimedValue {
        Thread.sleep(100)
        42
    }
    println(value)     // 42
    println(duration)  // 예: 103 ms
}

 

Duration 클래스의 구현 및 다양한 플랫폼에 대한 Clock 인터페이스 및 MonoClock 구현의 자세한 내용은 해당 KEEP을 참조하십시오. 이 API는 실험적이며 피드백에 따라 변경될 수 있습니다. 이를 사용하기 위해 해당 주석을 적용하여 명시적으로 동의해야 합니다.

피드백을 기다리고 있습니다!

 

비트 조작 함수


표준 라이브러리에는 이제 비트 조작을 위한 API가 포함되어 있으며 (보통 처음에는 실험적 상태로) 사용 가능합니다:

 

@UseExperimental(ExperimentalStdlibApi::class)
fun main() {
    val number = "1010000".toInt(radix = 2)
    println(number.countOneBits())
    println(number.countTrailingZeroBits())
    println(number.takeHighestOneBit().toString(2))
    println(number.rotateRight(3).toString(2))
    println(number.rotateLeft(3).toString(2))
}

 

Int, Long, Short, Byte 및 해당 언사인드 버전에 대한 유사한 확장 함수도 추가되었음을 유의하십시오.

 

IntelliJ IDEA 지원


개선된 Java to Kotlin 변환기


우리는 Java에서 Kotlin으로의 변환 후 수동으로 수정해야 하는 "빨간 코드" 양을 최소화하기 위해 Java-to-Kotlin 변환기를 개선할 계획입니다. 현재 변환기는 거의 항상 비-nullable 유형을 생성하므로 변환 후에 nullability 문제를 수동으로 수정해야 합니다. 이로 인해 나중에 nullability 불일치로 인한 런타임 오류가 발생할 수 있습니다.

새로운 개선된 버전의 Java-to-Kotlin 변환기는 코드에서 Java 유형 사용을 기반으로 더 정확하게 nullability를 추론하려고 시도합니다. 100% 오류가 없도록 하는 것이 목표가 아니라 컴파일 오류의 수를 줄이고 생성된 Kotlin 코드를 더 편리하게 작업할 수 있도록 하는 것이 목표입니다. 새 변환기는 많은 다른 알려진 버그도 수정했습니다. 예를 들어 이제 암시적 Java 형 변환을 올바르게 처리합니다.

미래에는 새 변환기가 기본 변환기로 사용될 것입니다. 이 릴리스에서는 미리보기로 사용할 수 있습니다. 사용하려면 설정에서 "Use New J2K (experimental)" 플래그를 지정하십시오.

테스트해보시고 피드백을 공유해 주시기 바랍니다!


디버깅 개선


Kotlin "Variables" 뷰가 표시할 변수를 선택하는 방법을 개선했습니다. 바이트 코드에는 많은 기술적인 정보가 있으므로 Kotlin "Variables" 뷰는 관련 변수만 강조 표시합니다. 이제 람다 내부에 중단점을 설정할 때 더 잘 작동합니다 (인라인 또는 비인라인). 람다 내부의 로컬 변수뿐만 아니라 외부 컨텍스트에서 캡처된 변수 및 외부 함수의 매개변수가 올바르게 표시됩니다:


필요한 경우 함수의 끝에 중단점을 설정할 수 있습니다:


디버거에서 "Evaluate expression" 기능 지원이 개선되어 로컬 확장 함수 또는 멤버 확장 프로퍼티의 액세서와 같은 많은 복잡한 언어 기능에 대해 개선되었습니다. 또한 "Evaluate expression"을 통해 변수를 수정할 수도 있습니다:

 


참고로 "Variables" 뷰에서 직접 변수를 수정할 수도 있습니다.


새로운 의도와 검사


새로운 의도와 검사가 추가되었습니다. 의도의 하나는 Kotlin 코드를 어떻게 작성해야 하는지 학습하는 데 도움을 주는 것입니다. 예를 들어 다음 의도는 범위를 수동으로 작성하는 대신 indices 속성을 사용하도록 제안합니다:

 


인덱스가 사용되지 않으면 루프를 요소를 기준으로 하는 for 루프로 자동으로 바꿀 수 있습니다.

IntelliJ IDEA는 이제:

원시 유형의 lateinit 프로퍼티를 by Delegates.notNull() 구문으로 자동으로 대체할 수 있습니다.

일반 프로퍼티를 지연 프로퍼티로 변환하고 다시 변환할 수 있습니다.

배열 비교에 대한 Java 메서드 사용 (Arrays.equals() 또는 Array.deepEquals()와 같은)을 감지하고 Kotlin 상응 메서드 (contentEquals 및 contentDeepEquals와 같은)로 대체하도록 제안합니다.

완성 목록에서 사용 중인 폐기된 가져오기를 강조 표시합니다.

 


IDE 작업의 일반적인 성능이 개선되었으며 대규모 사용 사례에서 "이동" 리팩터링을 호출하는 등 UI를 빙결시키는 여러 알려진 상황이 수정되었습니다.

 

Kotlin/JS


이 업데이트는 Windows에서 org.jetbrains.kotlin.js 플러그인을 사용하여 Kotlin/JS Gradle 프로젝트를 빌드하고 실행하는 지원을 추가합니다. 다른 플랫폼에서처럼 Gradle 작업을 사용하여 프로젝트를 빌드하고 실행할 수 있으며 Gradle 구성에서 필요한 NPM 종속성이 해결되고 포함됩니다. webpack-dev-server를 사용하여 애플리케이션을 시험해 볼 수도 있습니다(예: browserRun Gradle 작업을 호출하여). 또한 이 모든 것은 수동으로 node, npm 또는 yarn 배포를 설치하고 관리할 필요 없이 사용할 수 있습니다.

내부적으로 Kotlin/JS에 대한 성능 향상이 이루어졌으며 프로젝트의 점진적 컴파일 시간이 개선되었습니다. 이는 1.3.41과 비교하여 최대 30% 속도 향상을 기대할 수 있다는 것을 의미합니다.

NPM과의 통합이 개선되어 이제 프로젝트는 게으르게 및 병렬로 해결되며 동일한 프로젝트 내에서 변환 간의 추이적 종속성을 지원합니다.

새 버전에서는 생성된 아티팩트의 구조와 이름 지정에 대한 변경 사항도 포함됩니다. 생성된 아티팩트는 이제 분포 폴더에 번들로 포함되며 프로젝트 버전 번호와 archiveBaseName(기본값은 프로젝트 이름)이 포함됩니다. 예: projectName-1.0-SNAPSHOT.js.


Dukat


Dukat은 TypeScript 선언 파일(.d.ts)을 Kotlin 외부 선언으로 자동 변환하는 기능을 제공하며(ts2kt 명령 줄 도구를 대체함) Kotlin에서 JavaScript 생태계의 라이브러리를 안전하게 사용하기 위해 필요한 JS 라이브러리 래퍼를 수동으로 작성할 필요를 크게 줄입니다.

Kotlin/JS는 이제 Gradle 프로젝트용 dukat 통합을 실험적으로 제공합니다. 이 통합을 사용하면 Gradle에서 build 작업을 실행함으로써 npm 종속성에 대한 안전한 래퍼가 자동으로 생성되어 Kotlin에서 사용할 수 있습니다.

 


dukat는 아직 초기 단계에 있으므로 기본적으로 비활성화되어 있습니다. 프로젝트 루트 디렉터리의 gradle.properties 파일에 kotlin.js.experimental.generateKotlinExternals=true 줄을 추가하여 프로젝트에서 dukat를 활성화하십시오. 또한 Kotlin/JS 프로젝트에서 dukat 사용을 보여주는 예제 프로젝트를 준비했습니다. 시도해보고 피드백을 공유해 주세요.


Kotlin/Native


Kotlin/Native의 버전이 Kotlin의 버전과 다른 것을 발견하기 쉬웠지만 더 이상 그렇지 않습니다! Kotlin과 Kotlin/Native의 버전 체계가 이제 일치합니다. 이 릴리스는 Kotlin과 Kotlin/Native 이진 파일 모두에 버전 1.3.50을 사용하며 복잡성을 줄입니다.

이 릴리스는 모든 플랫폼, macOS 및 iOS를 포함한 모든 플랫폼에 대한 더 많은 사전 가져온 Apple 프레임워크를 제공합니다. Kotlin/Native 컴파일러는 이제 생성된 프레임워크에 실제 비트코드를 포함합니다.

Interop을 위한 몇 가지 개선 사항이 있습니다. 이제 프레임워크를 생성할 때 kotlin.Deprecated 주석을 지원하며 생성된 Objective-C 헤더 코드에는 경고가 표시되지 않습니다. 표준 라이브러리에는 Kotlin/Native 유형을 위한 kotlin.reflect.typeOf() 함수를 지원하는 업데이트가 있습니다. Worker 유형에는 지연 후 작업을 실행하는 데 사용할 수 있는 새 함수 executeAfter()가 추가되었습니다. 또한 Worker에서 작업 대기열을 명시적으로 처리하기 위해 processQueue() 함수를 호출할 수 있습니다.

이전 함수 ByteArray.stringFromUtf8() 및 ByteArray.stringFromUtf8OrThrow()은 이제 폐기되었습니다. 이전 릴리스에서 UTF-8 문자열을 Kotlin 문자열로 변환하기 위해 ByteArray.decodeToString() 함수(코틀린.text 패키지)를 추가했습니다. 이 함수는 NULL로 종료된 문자열을 지원하지 않으므로 1.3.50에서는 NULL로 종료된 UTF-8 문자열을 쉽게 처리하기 위한 새 함수를 추가했습니다. ByteArray.toKString() 함수(kotlinx.cinterop 패키지)를 사용하여 NULL로 종료된 UTF-8 문자열을 Kotlin 문자열로 변환할 수 있습니다. 필요한 경우 함수에 시작 및 끝 인덱스를 전달할 수 있습니다.

kotlin-platform-native Gradle 플러그인은 kotlin-multiplatform Gradle 플러그인의 대안으로 폐기되고 제거되었습니다. 이제 모든 멀티플랫폼 프로젝트 기능을 쉽게 활용할 수 있습니다. 이전으로 돌아가려면 마이그레이션 가이드 및 자세한 정보를 확인하세요.

마지막으로 Kotlin/Native 컴파일러와 인터옵 도구 성능이 이번 릴리스에서 개선되었다는 점을 기쁘게 알려드립니다.

 

멀티플랫폼 프로젝트


멀티플랫폼 프로젝트의 Kotlin/JVM 대상에 Java 컴파일을 포함시킬 수 있습니다. DSL의 newly added withJava() 함수를 호출하여 Kotlin/JVM 대상에 Java 컴파일을 구성할 수 있습니다. 기본적으로 src/<targetName>Main/java 및 src/<targetName>Test/java 경로를 사용합니다. 다음은 Java 컴파일이 활성화된 Kotlin/JVM 대상을 생성하는 전체 예제입니다:

plugins { 
    kotlin("multiplatform") version "1.3.50"
}
kotlin {
    jvm {
        withJava()
    }
}


Kotlin의 새 프로젝트 마법사는 새 프로젝트에 대한 Gradle Kotlin DSL을 생성합니다.

IntelliJ IDEA Ultimate에서 Kotlin/Native 코드 디버깅도 지원됩니다! 새로운 Native Debug for IntelliJ IDEA Ultimate 플러그인을 설치해보세요. Kotlin/Native 실행 구성과 디버그 작업을 지원해야 합니다.

 

 

스크립팅


이 릴리스에서는 스크립팅 및 REPL 지원에서 여러 기능과 개선 사항이 추가되었습니다. 애플리케이션에 대한 스크립팅 언어로 Kotlin을 사용하는 것이 더욱 쉬워졌습니다! 이제 스크립팅 지원이 기본으로 작동합니다. 기본 JSR-223 구현 라이브러리를 게시했으므로 애플리케이션에 Kotlin 스크립팅 지원을 추가하려면 kotlin-scripting-jsr223를 종속성으로 추가하고 javax.script API와 함께 사용하면 됩니다.

JSR-223 API를 통해 설정한 속성은 이제 스크립트에서 일반적인 Kotlin 속성으로 액세스할 수 있습니다(이전에는 바인딩 맵을 사용해야 했습니다):

 

val engine = ScriptEngineManager().getEngineByExtension("kts")!!
engine.put("z", 42)
engine.eval("""println("answer = $z")""")


Kotlin 1.3에서 소개된 kotlin-main-kts 아티팩트는 기본 유틸리티 스크립트의 생성과 사용을 단순화하기 위해 사용되었으며, 이제 JSR-223 호스트로도 사용할 수 있습니다. 종속성 해결을 위한 Repository 및 DependsOn과 같은 주석 외에도 현재 스크립트로 다른 스크립트를 "import"하도록 지시하는 @Import 주석을 지원합니다:

// common.main.kts:
val foo = "common foo"

// script.main.kts:
@file:Import("common.main.kts")
val bar = "bar with $foo"


스크립팅에 대한 자세한 내용은 KEEP-75에서 확인할 수 있습니다. Kotlin 저장소에서 예제를 찾을 수 있습니다. 피드백은 Kotlin Slack의 #scripting 채널에서 공유해 주세요!


업데이트 방법


항상 play.kotl.in에서 Kotlin을 온라인으로 시도해 볼 수 있습니다.

  • Maven, Gradle 및 npm에서: 컴파일러와 표준 라이브러리의 버전으로 1.3.50을 사용하십시오. 여기에서 문서를 참조하세요.
  • IntelliJ IDEA 및 Android Studio에서: Kotlin 플러그인을 버전 1.3.50으로 업데이트하십시오. 도구 | Kotlin | Kotlin 플러그인 업데이트 구성 및 "지금 업데이트 확인" 버튼을 클릭하십시오.
  • Eclipse에서: Marketplace를 사용하여 플러그인을 설치하십시오.
  • 명령 줄 컴파일러는 Github 릴리스 페이지에서 다운로드할 수 있습니다.

 

새 릴리스로 인해 문제가 발생하는 경우 포럼, Slack (여기에서 초대 받기), 또는 이슈 트래커에서 도움을 요청하실 수 있습니다.

Kotlin을 사용해 보세요!

 

외부 기여자에 대한 감사의 말


특히 IntelliJ IDEA에 유용한 의도와 검사를 지속적으로 제공해 주는 Toshiaki Kameyama에게 감사드립니다.

이번 릴리스에 포함된 풀 리퀘스트를 제출한 모든 외부 기여자에게 감사의 말씀을 전합니다:

  • Steven Schäfer
  • pyos
  • Ivan Gavrilovic
  • Mads Ager
  • Ting-Yuan Huang
  • ilgonmic
  • Jiaxiang Chen
  • Mark Punzalan
  • Jake Wharton
  • Jeffrey van Gogh
  • Peter Xu
  • Amaury
  • Benjamin Orsini
  • Dereck Bridie
  • Eduard Wolf
  • George Gastaldi
  • Juan Chen
  • Kevin Peek
  • KilianCallebaut
  • Louis CAD
  • Martin Petrov
  • Matthew Runo
  • AJ Alt
  • Ty Smith
  • ghedeon
  • technoir
  • Dat Trieu

 

그들의 기여로 Kotlin 생태계가 더욱 강화되었습니다. 이러한 커뮤니티의 지원은 Kotlin의 성공에 큰 도움이 됩니다. 감사합니다!

 

원문

 

https://blog.jetbrains.com/kotlin/2019/08/kotlin-1-3-50-released/

반응형

댓글