본문 바로가기
Kotlin/Release Notes

[Kotlin Release Notes] First Look at Kotlin 1.4-M2: Standard Library Improvements

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

2020년 5월 12일

 

Kotlin 1.4와 다음 미리보기 버전 1.4-M2에서 계속해서 작업하고 있습니다. 현재로서는 이 미리보기에서의 몇 가지 표준 라이브러리 개선 사항을 소개하려고 합니다.

1.4-M2에서의 표준 라이브러리 주요 개선 사항은 다음과 같습니다:

  • 기존 API의 변경 - 시그니처 업데이트 및 새로운 상수 도입
  • 공통 라이브러리에 더 많은 함수 추가
  • 배열 및 컬렉션을 위한 새로운 함수
  • 속성 위임 개선 사항

 

비록 Kotlin 1.4-M2가 아직 릴리스되지 않았지만, Kotlin 플레이그라운드에 이 미리보기의 초기 버전을 배포했으므로 이 게시물에서 찾을 수 있는 모든 것을 시도해 볼 수 있습니다. 이 게시물의 코드 샘플도 이 새 버전에서 실행됩니다.

새 버전을 빨리 시도해보고 싶다면 Kotlin 블로그 뉴스레터를 구독하여 릴리스 날을 놓치지 않도록 하세요.

 

공통 라이브러리 확장


"공통" 코드에서 표준 라이브러리를 사용할 수 있습니다. 이 코드는 Android 및 iOS 또는 JVM 및 JS와 같은 다른 플랫폼 간에 공유되는 코드입니다. 점차적으로 공통 라이브러리를 확장하고 빠진 기능을 추가하거나 이동하고 있습니다.

Kotlin/JVM의 이전 appendln 구현은 시스템 종속적인 줄 바꿈 문자열 (\n은 UNIX 시스템에서, \r\n은 Windows에서 사용)을 추가했습니다. 그러나 운영 체제나 기반이 되는 플랫폼과 관계없이 동일한 동작을 보장하는 것이 공통 코드에 관한 경우 중요하다고 생각합니다. 이것이 appendln을 deprecated(비권장)하고 항상 단일 \n 문자로 줄을 종료하는 새로운 appendLine 함수를 선호하는 이유입니다.

 

println(buildString {
	appendLine("안녕하세요,")
	appendLine("Kotlin 1.4-M2")
})


JVM에서 플랫폼별 구분자가 필요한 경우에는 여전히 System.lineSeparator()를 사용할 수 있습니다.

공통 코드에서 사용할 수 있는 다른 함수들로는 주어진 스택 추적의 문자열 표현을 사용할 수 있게 해주는 것도 있습니다. Throwable.stackTraceToString() 확장은 해당 throwable과 그 스택 추적에 대한 자세한 설명을 반환하며, Throwable.printStackTrace()는 이 설명을 표준 오류 출력에 출력합니다.

공통 코드에서 Throwable.addSuppressed() 함수도 사용할 수 있으며, 이를 통해 예외를 전달하기 위해 억제된 예외를 지정할 수 있고, Throwable.suppressedExceptions 속성은 모든 억제된 예외 목록을 반환합니다.


새로운 배열 함수


다양한 컨테이너 유형을 다룰 때 일관된 경험을 제공하기 위해 배열에 대한 새로운 확장 함수를 추가했습니다.

  • shuffle() - 배열 요소를 임의의 순서로 배치합니다.
  • onEach() - 주어진 작업을 각 배열 요소에 수행하고 배열 자체를 반환합니다.

 

이러한 함수들은 리스트를 사용하는 사람들에게는 익숙할 것입니다. 배열에서도 동일한 방식으로 작동합니다.

 

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()
println(letters.contentToString())


또한 배열 하위 범위를 정렬하는 새로운 함수도 추가했습니다. 이전에는 fromIndex와 toIndex 매개변수를 사용한 sort() 함수가 JVM 전용이었습니다. 이제 이것은 공통이며, 이와 관련된 두 가지 새로운 함수가 추가되었습니다. 이 함수들은 분명히 이름에서 나타나는 방식으로 두 인덱스를 취하고 그 사이의 요소를 (fromIndex 포함, toIndex 제외) 다시 정렬합니다.

  • 배열 하위 범위를 위한 reverse() 함수는 하위 범위의 요소 순서를 반대로 뒤집습니다.
  • 배열 하위 범위를 위한 sortDescending() 함수는 하위 범위의 요소를 내림차순으로 정렬합니다.

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

 

표준 라이브러리의 컬렉션 API에 새로운 함수

 

표준 라이브러리의 컬렉션 API에 새로운 함수가 추가되었습니다.

- setOfNotNull()라는 새로운 집합 생성 함수가 도입되어 제공된 인수 중에서 모든 null이 아닌 항목으로 구성된 집합을 만듭니다.

 

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


- shuffled() 함수가 이제 시퀀스에 대해 사용 가능합니다.

 

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


- onEachIndexed() 및 reduceIndexedOrNull() 함수가 onEach() 및 reduceOrNull() 함수의 상반된 함수로 추가되었습니다. 이미 알고 있듯이 컬렉션 처리 함수의 이름에 Indexed가 포함되면 작업이 요소 인덱스를 매개변수로 갖는 것을 의미합니다.

 

val list = mutableListOf("a", "b", "c", "d").onEachIndexed { index, item ->
    println("$index:$item")
}

val emptyList = emptyList<Int>()
emptyList.reduceIndexedOrNull { index, a, b -> index + a + b } // null


- runningFold() 및 runningReduce() 함수는 scan() 및 scanReduce() 함수의 동의어로 추가되었습니다. 이러한 이름은 관련된 함수 fold() 및 reduce()와 더 일관성 있습니다. scan()은 이 작업에 대한 일반적으로 알려진 이름이므로 미래에는 runningFold()와 함께 사용 가능할 것입니다. 그러나 실험적인 scanReduce()는 곧 비권장되고 제거될 예정입니다.

 

val numbers = mutableListOf(0, 1, 2, 3, 4, 5)
val runningReduceSum = numbers.runningReduce() { sum, item -> sum + item} // [0, 1, 3, 6, 10, 15]
val runningFoldSum = numbers.runningFold(10) { sum, item -> sum + item} // [10, 10, 11, 13, 16, 20, 25]


이러한 새로운 컬렉션 API 함수는 다양한 상황에서 더 강력한 컬렉션 처리를 지원합니다.

 

기존 API 개선


Kotlin 1.4는 주요 "기능" 릴리스로, 언어에 새로운 기능을 추가하거나 표준 라이브러리에 새로운 함수나 인터페이스를 추가할 수 있습니다. 우리는 증분 릴리스(예: 1.3.70)에서만 새로운 실험적 선언을 추가합니다. Kotlin 1.3.70을 사용하여 코드를 작성하고 실험적 선언을 사용하지 않는다면 코드는 Kotlin 1.3.40을 사용하는 동료들에게는 문제없이 컴파일됩니다.

기능 릴리스는 마이너 릴리스와 동일한 엄격한 규칙을 준수할 필요가 없으므로 새로운 기능이나 API를 사용하는 경우, Kotlin 1.3 버전의 Kotlin 컴파일러는 Kotlin 1.4에서 작성된 코드를 컴파일하지 못할 수 있습니다. 이를 통해 개선을 위해 API를 변경할 수 있는 몇 가지 변경 사항을 도입할 수 있습니다. 우리는 역방향 호환성을 주의 깊게 관찰하여 이전 버전용으로 작성한 코드가 컴파일되고 이전처럼 작동하도록 노력합니다.

Kotlin 1.4에서는 몇 가지 함수를 개선하여 null을 허용하도록 했습니다.

 

val s: String? = null
println(s.toBoolean()) // false


Kotlin 1.3에서는 이 코드를 컴파일하지 못하며, String.toBoolean()의 수신자를 non-nullable로 요구합니다. Kotlin 1.4에서는 수신자를 nullable 문자열로 변경하였습니다: String?.toBoolean(). 그럼에도 불구하고 이전에 작성한 모든 코드는 Kotlin 1.4에서 컴파일되며 이전과 같이 작동합니다.

Doubles 및 Floats에서 정의된 다음 상수들은 이제 "실제" 상수입니다.

 

println(Double.NaN)  // NaN
println(Double.NEGATIVE_INFINITY)  // -Infinity
println(Double.POSITIVE_INFINITY > Double.MAX_VALUE)  // true
println(Double.SIZE_BITS) // 64


이제 이들은 const 변수로 정의되어 있으므로 주석 인수로 사용할 수 있습니다.

SIZE_BITS와 SIZE_BYTES는 Double 및 Float의 새로운 상수입니다. 이들은 이진 형식으로 표현된 유형의 인스턴스를 나타내는 데 사용되는 비트 및 바이트 수를 포함합니다.

주의할 점은 Kotlin/JS에서 Float.MAX_VALUE와 Float.MIN_VALUE 값을 변경했다는 것입니다. 이전에는 JavaScript Number.MAX/MIN_VALUE와 동일한 값이었으며, Kotlin/JS에서 Float는 본질적으로 Double과 동등합니다. 이제 이러한 Float 범위 상수는 모든 플랫폼에서 동일합니다.


maxOf() 및 minOf()의 가변 인수 지원


표준 라이브러리의 maxOf() 및 minOf() 함수는 두 값 중 큰 값과 작은 값을 찾습니다. 1.4-M1부터 maxOf()와 minOf()는 가변 인수(vararg)를 허용하여 숫자 또는 다른 비교 가능한 항목의 모든 집합에 사용할 수 있도록 합니다.

 

val max = maxOf(1, 2, 3, 4)
println(max)


속성 위임 개선


Kotlin에서 위임된 속성은 인터페이스가 아닌 규칙을 통해 작동합니다. 위임으로 사용하려는 유형은 필요한 인터페이스를 구현하는 대신 연산자 함수를 정의해야 합니다. 이것은 유연성을 제공하지만 많은 실제 사용 사례에서 여전히 인터페이스를 사용하는 것이 도움이 됩니다.

Kotlin 1.4에서는 이러한 보완적인 인터페이스를 더 효율적으로 사용할 수 있게 하였습니다. 새로운 PropertyDelegateProvider 인터페이스를 도입하고 ReadWriteProperty는 이제 ReadOnlyProperty를 상속합니다. 자세한 내용은 계속 읽어보세요.


위임 표현식


ReadWriteProperty와 ReadOnlyProperty 인터페이스는 사용자 정의 클래스나 익명 객체로 속성 위임을 정의하는 데 편리합니다.

 

fun myDelegate(): ReadWriteProperty<nothing?, int=""> =
object : ReadWriteProperty<nothing?, int=""> {
...
}

val foo: Int by myDelegate()
var bar: Int by myDelegate()


1.4부터 ReadWriteProperty는 ReadOnlyProperty를 상속하므로 위임 표현식을 사용할 때 더 많은 유연성을 제공합니다. 예를 들어 위의 myDelegate() 호출을 ReadOnlyProperty가 예상되는 곳에 전달할 수 있습니다.

여기서 "읽기 전용"은 Kotlin에서 "불변"과 같지 않다는 점을 강조하고 싶습니다. 읽기 전용은 "이 인터페이스는 해당 객체에 대한 읽기 전용 액세스만 제공합니다"를 의미합니다.


위임 제공


위임을 제공하는 메커니즘을 사용하여 "위임" 개체를 생성하는 로직을 확장할 수 있습니다. 이 작업 방법의 자세한 내용은 문서에서 확인할 수 있습니다. 1.4에서는 이 메커니즘을 좀 더 편리하게 만들기 위해 새로운 PropertyDelegateProvider 인터페이스를 추가하였습니다. 이것은 위의 myDelegate() 예제에서 보았던 것과 유사한 익명 객체를 사용하는 경우에 유용합니다.

 

다른 속성에 위임


1.4부터 속성은 직접 getter와 setter를 다른 속성에 위임할 수 있습니다. 이것은 예를 들어 역방향 호환 방식으로 속성 이름을 변경하고자 할 때 유용할 수 있습니다. 새로운 속성을 도입하고 이전 속성에 @Deprecated 주석을 추가하고 구현을 위임합니다.

 

class MyClass {
	var newName: Int = 0
	@Deprecated("Use 'newName' instead", ReplaceWith("newName"))
	var oldName: Int by this::newName
}

fun main() {
	val myClass = MyClass()
	// Notification: 'oldName: Int' is deprecated.
	// Use 'newName' instead
	myClass.oldName = 42
	println(myClass.newName) // 42
}


이전에 설명한 컴파일된 위임 속성을 위한 최적화는 이 경우에도 작동합니다. 위임 연산자 구현은 위임되는 속성(oldName)에 대한 정보를 사용하지 않으므로 컴파일러가 해당 정보를 포함한 KProperty 인스턴스를 생성할 필요가 없습니다. 미래에는 위임자(newName)에 대한 추가적인 KMutableProperty 인스턴스를 생성하지 않도록 할 수도 있을 것입니다.


시도 방법


모든 설명한 변경 사항은 Kotlin 1.4-M2 미리보기의 일부입니다. 그러나 play.kotl.in에서 이미 온라인으로 시도할 수 있습니다. 설정에서 버전 1.4-M2를 선택하도록만 하세요.


프리 릴리스 노트


역방향 호환성 보장은 프리 릴리스 버전에는 해당하지 않음을 참고하세요. 피드백을 기반으로 이후 릴리스에서 기능 및 API가 변경될 수 있습니다.


피드백 공유


이슈 트래커에서 제보해주신 모든 버그 보고에 감사드립니다. 중요한 문제를 모두 해결하기 위해 최선을 다하겠습니다. 그래서 또 다른 Kotlin 릴리스를 기다릴 필요 없이 해결될 것입니다.

Kotlin Slack의 #eap 채널에 참여하셔서 질문을 하거나 토론에 참여하거나 새로운 미리보기 빌드 알림을 받아보실 수 있습니다.

Kotlin을 함께 해보세요!

 

원문

 

https://blog.jetbrains.com/kotlin/2020/05/1-4-m2-standard-library/

반응형

댓글