본문 바로가기
Kotlin/Release Notes

[Kotlin Release Notes] Kotlin 1.3.60 Released

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

2019년 11월 18일

 

오늘은 Kotlin 1.3.60 새 릴리스를 소개하게 되어 기쁩니다. 이번 릴리스는 품질 향상뿐만 아니라 다음과 같은 내용에 중점을 두고 있습니다:

  • 인라인 클래스의 비교 최적화
  • 디버깅, J2K 변환기 및 Kotlin 스크립트용 도구 개선
  • 더 많은 Kotlin/Native 플랫폼/대상 지원
  • Kotlin/MPP IDE 경험 개선
  • Kotlin/JS에 대한 소스 맵 지원 및 플랫폼 테스트 러너 통합 개선
  • Kotlin 1.4의 일부 이미 구현된 기능 미리보기

 

변경 사항의 전체 목록은 변경 로그에서 확인할 수 있습니다. 항상 외부 기여자분들에게 감사드립니다.

이제 자세한 내용을 살펴보겠습니다.


언어 변경 사항


증분 릴리스는 인라인 클래스와 같은 작은 개선 사항(오류 메시지 혼란을 줄이는 것과 같은)이나 실험적 기능 업데이트를 제외하고는 언어 변경 사항을 가져오지 않습니다. Kotlin 1.4에서 무슨 일이 일어날지 미리 엿보기 위해서는 아래 섹션을 읽어보세요.


인라인 클래스 개선


인라인 클래스 두 인스턴스의 동등성 비교는 기존 값의 불필요한 박싱을 일으켰습니다. 1.3.60부터 값 비교가 최적화되었습니다.

 

inline class MyClass(val value: Int) {
    // 컴파일러에 의해 생성됨:
    // public static final boolean equals-impl0(int p1, int p2) {
    //   return p1 == p2;
    // }
}

fun main() {
    val first = MyClass(1)
    val second = MyClass(1)
    println(first == second)
    // 내부에서 호출됨:
    // MyClass.equals-impl0(first, second)
}

 

바이트 코드에서는 각 인라인 클래스에 대해 기반 값의 비교를 수행하는 특수 정적 메서드 equals-impl0이 생성됩니다. 언박싱된 인스턴스에서 동등성 비교를 사용할 때 추가 박싱을 피하기 위해 내부에서 호출됩니다.

현재 인라인 클래스의 equals/hashCode를 재정의할 수 없습니다. 생성된 equals-impl0 메서드는 단순히 값들을 비교합니다. 미래 버전에서 인라인 클래스에 대한 사용자 정의 equals를 지원하면 같은 equals-imp0 메서드가 내부적으로 사용될 것으로 예상됩니다.

호환성을 위해 이 최적화는 Kotlin 1.4부터 kotlin.Result 클래스에 대해 작동합니다.

 

오류 메시지 개선


가끔 컴파일러 오류 메시지를 읽을 때 오류가 발생한 이유가 명확하지 않을 때가 있습니다. 이러한 경우를 해결하고 혼란을 줄 수 있는 모든 오류 메시지를 개선하려고 노력합니다.

Kotlin은 후행 람다 규칙을 지원합니다: 람다는 괄호 외부로 이동할 수 있습니다. 이러한 람다는 다음 줄에서도 시작될 수 있습니다. 이 규칙은 때로는 컴파일러가 다음 줄의 중괄호가 해당 함수의 람다 인수여야 한다고 가정하면서 혼란을 야기할 수 있습니다.

 


이제 이러한 경우를 위한 오류 메시지가 개선되었으며 이를 자동으로 수정할 수 있습니다. 이전 줄 끝에 세미콜론을 삽입하면 됩니다. (네, Kotlin에서는 가끔 세미콜론이 필요합니다!)

또 다른 중요한 경우는 가변 변수를 게으르게 만들려고 시도하는 것입니다. 게으른 변수는 읽기 전용으로 설계되었지만 이로 인해 혼란이 발생할 수 있습니다. 이제 오류 메시지가 개선되었으며 자동 빠른 수정이 제공됩니다.

 


만약 혼란스럽거나 유용한 빠른 수정이 빠져 있다고 생각하는 다른 사용 사례가 있는 경우, 이러한 요청을 이슈 트래커에 기록해 주시기 바랍니다.

 

IntelliJ IDEA 지원


스크래치 및 워크시트


스크래치 파일을 다시 디자인하고 개선했습니다. 이를 통해 코드베이스에서 작은 실험을 수행할 수 있습니다. 이제 결과를 보기 쉽게 다른 창에 표시됩니다. 여러 줄 출력은 줄바꿈되며 지정된 줄에 대한 출력이 강조 표시됩니다.

 


그러나 때로는 스크래치 파일이 잘 작동하지 않는 경우가 있습니다. 프로젝트 외부에서 정의되지 않고 프로젝트의 일부로 사용하는 것이 더 좋을 때가 있습니다. 교육 목적, 데모 프로젝트 생성 또는 프리젠테이션 중에 특히 유용할 수 있습니다. 이러한 모든 경우에 적합한 Kotlin 워크시트를 사용하세요.

 


Kotlin 워크시트는 개념적으로 기존 스크래치 파일과 기술적으로 매우 유사합니다. 코드베이스를 사용하여 코드를 실험하고 결과를 즉시 볼 수 있습니다. 두 가지의 주요 차이점 중 하나는 워크시트가 프로젝트의 일부인 반면 스크래치는 프로젝트 외부에서 사용되도록 설계되었다는 것입니다.


build.gradle.kts


Kotlin Gradle 빌드 스크립트의 사용자 경험을 향상하기 위해 작업 중입니다. 이미 완성 및 강조 표시 성능을 개선했으며, Gradle과 긴밀히 협력하여 더 개선할 예정입니다.

 

 

디버깅 개선


이제 Kotlin 코드에 함수 중단점을 설정할 수 있습니다. 그런 다음 디버거는 해당 함수에 진입하거나 빠져 나갈 때 실행을 중단합니다. 필요한 경우 추가 진입 조건도 설정할 수 있습니다.

 

 

완성 및 import 개선


패키지 이름이 로컬 변수 이름과 일치하는 경우와 같은 몇 가지 알려진 버그가 수정되었습니다. 이제 패키지 이름과 로컬 변수 이름이 일치하는 경우 완성이 올바르게 작동합니다:


이제 enum을 위한 typealias를 정의하면 완성에서 해당 멤버가 올바르게 표시됩니다:

 


만약 연산자 함수를 간결한 연산자 구문을 통해 invoke와 같이 사용한다면 IntelliJ IDEA가 자동으로 가져오도록 제안합니다.

 

새로운 Java-to-Kotlin 변환기


우리는 새로운 Java-to-Kotlin 변환기에 대한 훌륭한 작업을 수행했습니다. 정적 가져오기와 같은 여러 코너 케이스 문제가 수정되었으며 컬렉션의 사용 분석도 개선되어 컬렉션이 변환 후에 가변 또는 읽기 전용이 되더라도 올바르게 처리됩니다. 이 컬렉션 자체가 제네릭 인수인 경우에도 올바른 분석이 수행됩니다.

이제 여러 파일을 한꺼번에 변환할 때 이들은 함께 분석되며 다른 파일에서의 사용 사례가 최종 결과에 영향을 미칩니다. 예를 들어, Java에서 foo 함수에 String 인수로 null을 전달하면 함수와 그 사용 사례를 함께 변환한 후에 변환된 Kotlin 함수는 null이 가능한 String?을 인수로 사용할 것입니다.

 


새로운 변환기가 이제 기본으로 사용됩니다.


Eclipse IDE 플러그인 업데이트


우리는 kotlin-eclipse 플러그인이 이제 단일 모듈에 대한 실험적 증분 컴파일을 지원한다는 것을 기쁘게 알려드립니다. 이를 시도하려면 Eclipse 속성의 Kotlin | Building 섹션에서 "증분 컴파일" 확인란을 선택하십시오. 이것은 여전히 실험적인 기능이므로 피드백을 기다리고 있을 것입니다!

 

 

Kotlin/Multiplatform


우리는 MPP (Multiplatform) 도구에 많은 주력을 기울였으므로 지난 시간 동안 기대한 대로 작동하지 않은 경우, 다시 한 번 시도해 보시기 바랍니다!

향후 릴리스에서 Kotlin의 멀티플랫폼 측면에 새로운 강력한 기능이 추가될 수 있지만, 이 릴리스에서는 IDE 내에서의 사용 편의성을 크게 향상시켰습니다. 특히 일부 "expect 생성" 빠른 수정을 크게 개선했습니다.


Kotlin/Native


Kotlin/Native 컴파일러가 몇 가지 새로운 기능을 얻었습니다.

  • 최신 도구 비트와 호환성: XCode 11 및 LLVM 8.0.
  • 다양한 새로운 플랫폼/타겟:
    • watchOS
      • watchos_x86
      • watchos_arm64
      • watchos_arm32
  • tvOS
    • tvos_x64
    • tvos_arm64
  • Android (native)
    • android_x86
    • android_x64
  • 릴리스 이진 코드에 대한 iOS 크래시 보고서의 실험적 심볼화 (LLVM 인라인 코드 포함, 이는 XCode가 디코딩할 수 있는 것보다 한 단계 더 나아간 것입니다).
  • Kotlin 객체에 대한 Objective-C 약한/공유 참조의 스레드 안전한 추적.
  • suspend callable 참조 지원.
  • "큰 아리티"를 가진 함수 (JVM 제한과 동등)
  • 모든 컨텍스트/스레드에 대한 작업 큐 연결 기능, Worker.start를 통해 임시로 생성된 것뿐만 아니라.

 

일반적인 Kotlin/Multiplatform 명령행 파서


일부 사용자는 kotlinx.cli 프로젝트가 몇 달 동안 활성화되지 않은 것을 알아챌 수 있습니다. 이제 해당 프로젝트 코드가 (대부분) 다시 작성되었으며 Kotlin/Native 컴파일러의 이번 릴리스에도 포함되었습니다.

우리는 초기 사용자의 피드백을 환영합니다! 이것이 어떻게 사용되는지 일부 샘플 (테트리스 게임, CSV 파서, 비디오 플레이어)에서 확인하고 심지어 내부적으로도 사용할 수 있습니다.


성능


비록 Kotlin/Native 컴파일러가 아직 성능을 깊게 최적화되지는 않았지만, 이 릴리스에서는 몇 가지 개선 사항을 가져와 몇 가지 인상적인 속도 향상을 가져옵니다.

특히 대형 프로젝트의 경우 네이티브 라이브러리를 직접 klib에서 생성함으로써 컴파일 속도가 증가했습니다.

런타임 성능도 향상되었습니다. 인터페이스 호출은 이제 최대 5배 더 빨라졌으며 형식 검사는 최대 50배 더 빨라졌습니다!


버그 수정


- 인라인 코드에 대한 누락된 디버그 정보

 

일부 인라인 최적화가 소스 라인 번호와 이진 코드 위치 간의 매핑을 업데이트하는 것을 잊어버려 브레이크 포인트가 "목적지를 넘어서는" 오동작을 일으켰습니다. 이제 이들을 더 열심히 처리하도록 가르쳤으며 이제 브레이크 포인트가 예상대로 작동합니다.


- 가변 길이 인수에 null 전달

 

가변 길이 인수를 사용하는 함수에 null이 인수로 전달되면 컴파일러는 어떤 유형을 할당해야 하는지 명확하지 않았으며 오류가 발생할 때 이에 반발했습니다. 이제 이러한 값을 COpaquePointer로 처리합니다. 이는 C에서의 void *와 동등한 무타입/void 포인터입니다.


- iOS/macOS에서 음수 값을 언박싱하는 문제

 

때로는 음수 바이트를 처리할 때 크래시가 발생하며 이는 LLVM 버그였으며 이에 대한 수동 해결책을 제안했습니다. 이제부터 이번 릴리스부터 컴파일러가 자동으로 이 해결책을 적용합니다.


Kotlin/JS


Kotlin/JS 분야에서는 품질을 개선하고 org.jetbrains.kotlin.js 플러그인을 사용하는 작업을 단순화하는 데 중점을 두는 새로운 변경 사항이 적용되고 있으며, 특히 소스 맵 지원 및 테스트 러너 개선이 주목할 만합니다.


소스 맵


Kotlin 1.3.60에서는 org.jetbrains.kotlin.js Gradle 플러그인을 통해 JavaScript를 대상으로하는 코드에 대한 소스 맵이 자동으로 생성됩니다. 이로써 자신의 코드를 디버깅할 때 훨씬 편리해지며 오류가 발생하면 가독성 있는 스택 트레이스를 제공하며 브라우저를 대상으로 하는 경우 개발자 도구에서 제공하는 슈퍼파워를 제공합니다. 이로써 중단점, 코드 주석, 로컬 범위 정보 등을 지원합니다. 또한 JS 대상의 테스트 작업을 간소화합니다. 다음 섹션에서 자세히 살펴보겠습니다.

 

 

테스트 러너 개선


JS 플랫폼에서 테스트를 실행할 때 표준 출력은 일반적인 채널 (즉, 로그, 경고, 오류)에서 생성되며 생성된 Gradle 보고서에 포함됩니다. 이 기능은 Node.js 및 브라우저 대상으로 사용할 수 있습니다. 소스 맵과의 통합을 통해 테스트의 스택 트레이스가 가독성 있으며 Kotlin 소스에 직접 파일 이름과 줄 번호가 가리킵니다.

 


JS 대상의 테스트를 위한 테스트 필터링 지원은 모든 작업을 한 번에 실행할 필요 없이 테스트를 선택적으로 실행할 수 있도록 합니다. --tests 플래그를 통해 Gradle 명령 줄 인터페이스를 통해 이 기능을 사용할 수 있습니다.

./gradlew browserTest --tests AppTest.testOperationExecution


또는 IntelliJ IDEA의 gutter 아이콘을 사용하여 개별 테스트 또는 특정 세트의 테스트를 실행할 수 있습니다.

 


Kotlin 1.4에서 예정된 변경 사항


Kotlin 1.4는 2020년 중 어느 시점에 릴리스될 예정입니다. 그러나 해당 언어 버전을 지정하여 이미 구현된 몇 가지 기능을 시도할 수 있습니다.

 

compileKotlin {
	kotlinOptions {
		languageVersion = "1.4"
	}
}


NPE 단언


apiVersion을 1.4로 설정하면 이전 메시지 "JavaCode.getNull() must not be null"을 통해 설명한 null-check 최적화가 변경된 동작을 관찰할 수 있습니다. 이제 이전에 IllegalStateException을 throw했던 코드가 NullPointerException을 throw합니다.

 

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

fun duplicate(s: String) = s + s

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

 

break 및 continue의 when 내부에서 허용


Kotlin 1.4의 언어 변경 중 하나는 when 내부에서 break와 continue를 허용하는 것입니다. 현재 break와 continue 표현식은 레이블이 없는 경우 금지되어 있습니다. 그러나 레이블을 사용하는 것이 다소 불편하게 느껴졌으므로 Kotlin 1.4에서는 바깥쪽 루프 내에서 기대하는 의미로 break와 continue가 동작합니다.

fun foo(list: List<int>) {
	for (i in list) {
		when (i) {
			42 -> continue
			else -> println(i)
		}
	}
}


when 내부의 fall-through 동작은 더 많은 설계를 필요로 합니다.


tail-recursive 함수에 대한 변경 사항


tail-recursive 함수에 대한 "모서리 케이스" 행동 특이 사항을 수정할 예정입니다.

 

기본 값의 초기화 순서


tailrec 함수 내에서 기본 값에 부작용이 있는 경우 변경 사항이 눈에 띄게 나타납니다. Kotlin 1.3에서는 tailrec 함수 내에서 기본 값의 초기화 순서가 잘못되어 있습니다. 기본 값은 처음부터 끝까지 초기화되어야 하는데, 이는 일반 함수에서와 동일하게 마지막부터 처음까지 초기화됩니다.

다음 예제에서이 변경 사항을 확인할 수 있습니다.

var counter = 0
fun inc() = counter++

tailrec fun test(i: Int, x: Int = inc(), y: Int = inc()) {
	println("x: $x, y: $y")
	if (i > 0) test(i - 1)
}

fun main() {
	test(1)
}


Kotlin 1.3에서 출력은 다음과 같습니다.

x: 0, y: 1
x: 3, y: 2


Kotlin 1.4에서 출력은 다음과 같습니다.

x: 0, y: 1
x: 2, y: 3


이러한 경우는 실제로 매우 드물게 발생할 것으로 예상됩니다. (그러나 어떤 이유로든 이러한 언어 기능의 복잡한 조합을 사용하는 경우에는 이 변경 사항을 참고하십시오.)


열린 tailrec 함수를 금지


Kotlin 1.3에서는 open 및 tailrec 수정자를 결합하는 것이 경고였지만, Kotlin 1.4에서는 오류가 됩니다. 이것은 "중요한 변경 사항"입니다. 이전에 작동하던 코드가 더 이상 작동하지 않는다는 의미이지만, 실제로는 이 경우가 실제로 사용되지 않을 것으로 예상됩니다.

open 및 tail-recursive 함수가 주로 tail-recursive 또는 open 함수로 동작해야 하는지 여부는 명확하지 않습니다. Kotlin 1.3에서는 open 수정자가 "무시"되지만 이로 인해 혼란스러운 동작이 발생합니다:

open class A {
    open tailrec fun foo(count: Int) { // 주의: open 및 tailrec 수정자
        println("A.foo($count)")
        if (count > 0) foo(count - 1)
    }
}

class B : A() {
    override fun foo(count: Int) {
        println("B.foo($count)")
    }

    fun callSuperFoo(count: Int) = super.foo(count)
}

fun main() {
    B().callSuperFoo(3)
}


이 코드의 출력은 다음과 같습니다:

 

A.foo(3)
A.foo(2)
A.foo(1)
A.foo(0)


foo 함수는 tail-recursive 함수로 예상대로 동작합니다. 함수는 자체를 호출하고 이 호출은 내부적으로 최적화됩니다.

그러나 A 클래스의 foo 함수에서 tailrec 수정자를 제거하면 출력이 다음과 같이 됩니다:

 

A.foo(3)
B.foo(2)


이제 foo 함수는 열린 함수로 예상대로 동작합니다. 먼저 super.foo(count)는 부모 A 클래스에서 함수를 명시적으로 호출합니다. 그런 다음 foo(count - 1)은 B 하위 클래스의 함수를 호출하는 가상 호출입니다.

이러한 동작이 혼란스러울 수 있으므로 Kotlin 1.4에서는 open 및 tailrec를 동시에 사용하는 것을 금지합니다.

 

업데이트 방법

 

  • Maven, Gradle 및 npm에서: 컴파일러 및 표준 라이브러리의 버전으로 1.3.60을 사용하십시오. 자세한 내용은 문서를 참조하십시오.
  • IntelliJ IDEA 및 Android Studio에서: Kotlin 플러그인을 버전 1.3.60으로 업데이트하십시오. 도구 | Kotlin | Kotlin 플러그인 업데이트 구성을 사용하고 "지금 업데이트 확인" 버튼을 클릭하십시오.
  • Eclipse에서: 마켓 플레이스를 통해 플러그인을 설치하십시오.
  • 명령 줄 컴파일러에서: GitHub 릴리스 페이지에서 명령 줄 컴파일러를 다운로드할 수 있습니다.

 

새 릴리스와 관련된 문제가 발생하면 포럼에서 도움을 요청하거나 (여기에서 초대장을 받으실 수 있음) 이슈 트래커에 문제를 보고하십시오.

Kotlin을 즐기세요!

 

외부 기여


특히, 인라인 클래스의 등가 비교 최적화를 구현한 Steven Schäfer에게 감사드립니다.

또한, 이 릴리스에 포함된 외부 기여자들에게 감사드립니다:

  • Toshiaki Kameyama
  • Ivan Gavrilovic
  • Mads Ager
  • Mark Punzalan
  • pyos
  • Jake Wharton
  • Yanis Batura
  • Kristoffer Andersen
  • Sebastian Schuberth
  • Kevin Bierhoff
  • scache
  • keijumt
  • Louis CAD
  • Matthew Gharrity
  • Jim Sproch
  • Jim S
  • Dereck Bridie
  • Sascha Peilicke
  • SatoShun
  • Dat Trieu
  • Burak Eregar
  • Ty Smith
  • Vladimir Krivosheev
  • Alex Chmyr

 

이 모든 외부 기여자들에게 감사의 말씀을 드립니다.

 

원문

 

https://blog.jetbrains.com/kotlin/2019/11/kotlin-1-3-60-released/

반응형

댓글