본문 바로가기
Kotlin/Release Notes

[Kotlin Release Notes] A major release just around the corner — meet Kotlin 1.3-M2

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

2018년 8월 29일

 

Kotlin 1.3으로 전진하며, 새로운 기능을 공개하고 이미 발표된 기능의 안정성을 향상시킨 두 번째 마일스톤 릴리스인 Kotlin 1.3-M2를 기쁘게 발표합니다. 몇 가지 주요 하이라이트:

  • Contracts는 스마트 캐스트 및 다른 컴파일 타임 분석을 개선합니다.
  • 부호 없는 타입과 컬렉션을 위한 새로운 표준 라이브러리 함수
  • 코루틴을 위한 리플렉션
  • 1.3에서 졸업하는 새로운 코루틴에 대한 이전 코루틴으로의 마이그레이션 레이어
  • 인라인 클래스 관련 다양한 버그 수정

 

이번 릴리스에 포함된 외부 기여자의 커밋을 포함하여 이번 릴리스에 포함된 모든 변경 사항의 전체 목록은 변경 로그에서 확인하실 수 있습니다. 새로운 기능 또는 이번 릴리스에서 발생하는 문제에 대한 피드백을 감사히 받고 있습니다.


Contracts


1.3-M2에서는 새로운 실험적인 기능인 contracts를 소개합니다. 다음은 동기부여되는 예시를 살펴보겠습니다:

 

fun test(x: Any?) {    
    require(x is String) 
    println("'$x'의 길이는 ${x.length}")
}

 

여기서 require()는 Standard Library에 정의된 함수로, 인자가 false인 경우 예외를 던집니다 (assert()와 유사하지만 -ea 플래그에 의존하지 않습니다). 따라서 println()이 실행된다면 x는 반드시 String이어야 하며, 이 줄에서 스마트 캐스트가 매우 논리적일 것입니다. 문제는 컴파일러가 일반적으로 함수 시그니처만 살펴보기 때문에 require()가 어떻게 구현되었는지 알 수 없으며, 따라서 x가 이제 확실히 String의 인스턴스임을 추론할 수 없습니다.

contracts를 사용하면 함수가 컴파일러에게 "나는 이렇게 스마트 캐스트에 영향을 미친다" 또는 "나는 이 람다를 즉시 한 번만 실행한다"와 같은 정보를 전달할 수 있습니다. contracts는 함수 시그니처를 통해 사용 가능한 추가 의미를 호출 사이트에서 유용하게 사용할 수 있도록 함으로써 함수 시그니처에 있는 유형 정보를 보완합니다 ("nullable T의 리스트를 취하고 Boolean을 반환합니다"와 같은). 예를 들어, "나는 비-NULL 리스트에 대해서만 false를 반환합니다"와 같은 정보도 제공합니다.

현재 Kotlin 표준 라이브러리는 이미 여러 함수에 contracts를 추가하여 제공하고 있습니다. 이들은 실험적인 플래그와 상관없이 작동합니다.

현재 Kotlin 컴파일러는 contracts를 두 가지 유형의 개선을 위해 사용합니다:

- 스마트 캐스트를 더욱 더 똑똑하게 만듭니다:

 

fun test(x: List<Int>?) {
    // 함수가 false를 반환하는 경우 값은 확실히 NULL이 아닙니다:
    if (!x.isNullOrEmpty()) {
        println(x.size) // 스마트 캐스트로 not-null로 변경!
=
    }
}

fun test(x: Any?) {
    // 함수가 (예외를 던지지 않으면) 인수는 true입니다:
    require(x is String) 
    println(x.length) // 여기도 스마트 캐스트!
}


- 변수 초기화를 더 정확하게 분석합니다:

val x: Int
synchronized(lock) {
    x = 42 // 컴파일러는 람다가 정확히 한 번 호출됨을 알고 있습니다!
}
println(x) // 이제 허용됩니다. 컴파일러는 'x'가 확실히 할당되었다고 알고 있습니다.


위에서 나열된 표준 함수의 contracts는 안정적이지만, 선언 위치 구문과 contracts를 정의하기 위한 API는 앞으로 변경될 수 있습니다. 사용자 정의 함수에 대한 contracts를 지정하려면 @ExperimentalContracts 주석을 사용하여 실험적인 모드에 참여해야 합니다:

 

import kotlin.contracts.*

@ExperimentalContracts
fun myRun(block: () -> Unit) {
    contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
    block()
}


contracts는 아직 선언 위치에서 검증되지 않기 때문에 정확한 정보를 제공하는 것은 사용자의 책임입니다. 문법, 호환성 보증 및 일반적인 설계 개요에 대해서는 해당 KEEP 문서를 참조하십시오.


코루틴 업데이트


Kotlin 1.3은 코루틴을 안정화하고 실험 기간 동안 얻은 피드백을 기반으로 API 및 ABI에서 여러 가지 변경을 수행하여 코루틴을 안정화합니다. 이전 마일스톤인 1.3-M1에서는 이전 실험적인 코루틴과 새로운 안정화된 코루틴 간의 마이그레이션 지원이 없었습니다. 이제 1.3-M2에서는 기존의 suspend 함수를 간단하게 호출할 수 있도록 이러한 지원을 제공합니다.

또한 Kotlin 리플렉션은 이제 코루틴에 대한 인트로스펙션을 지원하며 다음과 같은 메서드가 추가되었습니다:

  • KCallable.isSuspend
  • KCallable.callSuspend
  • KCallable.callSuspendBy


마지막 두 가지는 리플렉션을 통해 suspend 함수를 호출할 수 있도록 하기 위해 suspend로 표시되어 있습니다.


표준 라이브러리


부호 없는 타입


부호 없는 정수 타입을 지원하기 위한 라이브러리 지원이 개선되었습니다. 부호 없는 정수 타입과 배열이 일급 시민처럼 느껴지도록 하기 위해 도입된 내용을 살펴보겠습니다:

- 임의의 기수에 따라 부호 없는 정수를 문자열로 변환하는 함수 및 이를 역으로 수행하는 함수: UInt.toString(base), String.toUInt(base) 등

 

val uint = "4275878552".toUInt()
println(uint.toString(16))
println("FF".toUByte(16))


- 범위와 간격을 만드는 더 많은 방법을 지원하기 위한 확장 함수 until, downTo, step 및 reversed.

 

println(1u..UInt.MAX_VALUE)
println(1u until UInt.MAX_VALUE)
println(UInt.MAX_VALUE downTo 0u)
println(0u..UInt.MAX_VALUE step 10)
println((0u..UInt.MAX_VALUE step 10).reversed())


- 부호 없는 정수 배열은 List의 구조적 동등성 규칙을 구현하지 않으며, 이는 부호 있는 정수 배열과 동일합니다. 따라서 contentEquals(other), contentHashCode(), contentToString() 함수를 사용하여 이러한 배열을 비교하거나 내용을 문자열로 표시하는 것이 쉬워졌습니다.

 

val arr1 = UIntArray(5) { it.toUInt() }
val arr2 = uintArrayOf(0u, 1u, 2u, 3u, 4u)

println(arr1 contentEquals arr2)
println(arr1.contentToString())
println(arr1.contentHashCode())


- 주어진 크기의 부호 없는 정수 배열을 생성하는 데 사용되는 부가 생성자도 소개되었습니다.

 

val uintArrayOfZeroes = UIntArray(16)
println(uintArrayOfZeroes.contentToString())


- 부호 없는 정수 배열 및 하위 범위를 복사하는 확장 함수도 도입되었습니다: copyOf(), copyOf(newSize), copyOfRange(fromIndex, toIndex).

val array = ubyteArrayOf(0x01u, 0x02u, 0xFEu, 0xFFu)
println(array.copyOf().contentToString())
println(array.copyOf(2).contentToString())
println(array.copyOf(5).contentToString())
println(array.copyOfRange(2, 4).contentToString())


- 부호 없는 정수 배열을 부호 있는 정수 배열로, 그리고 그 반대로 다시 해석하는 확장 함수도 도입되었습니다: ByteArray.as/toUByteArray(), UByteArray.as/toByteArray() 등.

 

val ubyteArray = ubyteArrayOf(1u, 2u, 254u, 255u)
val byteArray = ubyteArray.asByteArray()
println(byteArray.contentToString())

byteArray[0] = -1
println(ubyteArray.contentToString())

 

두 개의 배열에서 객체 복사


- 특화된 부호 없는 정수 배열을 객체 배열로 변환하는 확장 함수도 소개되었습니다: UIntArray.toTypedArray() 등
두 개의 기존 배열 간에 요소를 복사하는 array.copyInto(targetArray, targetOffset, startIndex, endIndex) 함수는 기존 배열 유형을 포함한 기존 배열 유형에 대해 순수한 Kotlin에서 배열 기반 컨테이너를 구현하기 쉬워졌습니다.

val sourceArr = arrayOf("k", "o", "t", "l", "i", "n")
val targetArr = sourceArr.copyInto(arrayOfNulls<String>(6), 3, startIndex = 3, endIndex = 6)
println(targetArr.contentToString())

sourceArr.copyInto(targetArr, startIndex = 0, endIndex = 3)
println(targetArr.contentToString())

 

associateWith


- 키 목록이 주어지고 각 키를 일부 값과 연관시켜 맵을 구축하는 상황은 상당히 일반적입니다. 이전에는 associate { it to getValue(it) } 함수를 사용하여 수행할 수 있었지만, 이제 더 효율적이고 탐색하기 쉬운 대안인 keys.associateWith { getValue(it) }를 소개합니다.

 

val keys = 'a'..'f'
val map = keys.associateWith { it.toString().repeat(5).capitalize() }
map.forEach { println(it) }

 

ifEmpty, ifBlank 함수


- 컬렉션, 맵, 객체 배열, char 시퀀스 및 시퀀스는 이제 비어있을 때 수신자 대신 사용될 대체 값이 지정될 수 있는 ifEmpty 함수를 갖게 되었습니다.

 

fun printAllUppercase(data: List<String>) {
    val result = data
    .filter { it.all { c -> c.isUpperCase() } }
        .ifEmpty { listOf("<no uppercase>") }
    result.forEach { println(it) }
}

printAllUppercase(listOf("foo", "Bar"))
printAllUppercase(listOf("FOO", "BAR"))


- 문자 시퀀스 및 문자열은 ifEmpty와 비슷한 동작을 수행하지만 문자열이 비어있는 대신 모든 공백인지를 확인하는 ifBlank 확장 함수도 추가되었습니다.

 

val s = "    \n"
println(s.ifBlank { "<blank>" })
println(s.ifBlank { null })

 

Random API 개선


이전 1.3-M1 릴리스에서 도입된 Random 클래스에는 기여자 Leonardo Colman Lopes의 덕분에 부호 없는 숫자 및 부호 없는 바이트 배열을 생성하기 위한 확장이 추가되었습니다: nextUInt, nextULong 및 nextUBytes 확장 함수입니다.

val randomUInt = Random.nextUInt(0x8000_0000u..0xFF00_0000u)
println(randomUInt.toString(16))

val randomUBytes = Random.nextUBytes(4)
println(randomUBytes.contentToString())


또한 컬렉션, 배열 및 범위에서 모든 요소 중에서 무작위 요소를 얻을 수 있는 도움되는 확장 함수 random()도 도입되었습니다.

 

val sweets = listOf("nougat", "marshmallow", "oreo", "pie")
println("I'm going to taste some ${sweets.random()} today!")

val chars = 'a'..'z'
val randomChars = String(CharArray(10) { chars.random() })
println(randomChars)


Sealed 클래스 리플렉션


우리는 sealed 클래스의 모든 직접 하위 유형을 나열할 수 있는 새로운 kotlin-reflect API를 추가했습니다. 즉, KClass.sealedSubclasses입니다.


인라인 클래스


1.3-M2에서는 인라인 클래스를 시그니처에 사용하는 함수의 이름에 대해 자동 맹글링을 소개합니다. 이렇게 함으로써 인라인 클래스의 인라인 유형만 다르고 운반 유형은 다르지 않은 여러 오버로드가 있는 경우 플랫폼 시그니처 충돌을 방지합니다.

 

// All these types are inline classes mapped to String:
fun foo(x: UserName) { ... }
fun foo(x: Login) { ... }
fun foo(x: UserHash) { ... }


맹글링의 또 다른 이유는 Java에서 (우연한) 사용을 금지하기 위해서이며, 이는 인라인 클래스가 순수한 Kotlin 개념이기 때문에 원하지 않는 동작일 수 있습니다.
맹글링된 이름은 foo-<시그니처를 기반으로 한 일부 문자열>과 같은 형태를 가질 것입니다.

이번 릴리스의 인라인 클래스에 대한 두 가지 개선 사항은 다음과 같습니다:

  • 보조 생성자 지원
  • 자동으로 생성된 toString/equals/hashCode 함수

 

언어 변경 사항


1.3 및 이상 버전의 kotlinc에 대한 -language-version 플래그를 통한 이전 소스 언어 버전 지원이 폐기되었습니다. 이 변경 사항은 이전 대상 버전의 소스 코드 컴파일에만 영향을 미칩니다. 안정된 언어 버전으로 컴파일된 모든 이진 파일은 나중에 나올 kotlinc의 나중 버전에서 지원됩니다. Kotlin 1.0 및 1.1로 소스를 컴파일하려면 해당하는 이전 버전의 kotlinc를 사용할 수 있습니다.

또한 이번 업데이트에서는 안정적이고 정의된 동작을 보장하기 위해 몇 가지 새로운 제한이 추가되었습니다:

  • 루프, 인라인 람다 및 인라인 익명 함수 이외의 문에 대한 잘못된 레이블은 실제 의미나 사용 사례가 없었으며 이제 금지됩니다. (KT-22274)
  • 바이너리에 표시되지 않았던 어노테이션 생성자 매개변수에 대한 getter-targeted 어노테이션은 이제 어노테이션 클래스 getter 메서드에 작성됩니다. (KT-25287)
  • 데이터 클래스 생성자의 @get: 어노테이션에 누락된 미해결 참조 오류가 이제 올바르게 보고됩니다. (KT-19628)

 

사전 릴리스 참고 사항

 

사전 릴리스 버전에는 역호환성 보증이 포함되지 않습니다. 기능 및 API는 이후 릴리스에서 변경될 수 있습니다. 최종 RC에 도달하면 사전 릴리스 버전으로 생성된 모든 이진 파일이 컴파일러에 의해 금지될 것이며, 1.3‑Mx로 컴파일된 모든 것을 다시 컴파일해야 합니다.
그러나 1.2.x 및 이전 릴리스로 컴파일된 모든 코드는 다시 컴파일하지 않고도 완벽하게 작동합니다.


이를 사용해보는 방법


Maven/Gradle에서: 빌드 스크립트 및 프로젝트를 위해 http://dl.bintray.com/kotlin/kotlin-eap을 저장소로 추가하고, 컴파일러 플러그인 및 표준 라이브러리의 버전 번호로 1.3-M2를 사용하십시오.

IntelliJ IDEA에서: Tools → Kotlin → Configure Kotlin Plugin Updates로 이동한 다음 업데이트 채널 드롭다운 목록에서 "Early Access Preview 1.3"을 선택하고 업데이트를 확인하십시오.

 

원문

 

https://blog.jetbrains.com/kotlin/2018/08/kotlin-1-3-m2/

반응형

댓글