2022년 2월 5일 토요일

[코틀린] 1.5 변경사항

 

언어 문법

JVM records 지원

자바는 빠르게 발전하고 있고, 코틀린은 그 변화를 반영해주는 방향으로 함께 발전하고 있습니다. 이번 변경사항 중 하나는 자바의 record 지원입니다.

코틀린에서 일반적으로 클래스나 프로퍼티를 사용하는것처럼 자바의 record 클래스를 사용할 수 있습니다. data 클래스 에 @JvmRecord 어노테이션을 선언해주면 됩니다.

@JvmRecord
data class User(val name: String, val age: Int)

Note : record는 코틀린의 data클래스와 유사하게, 순수 데이터만을 담도록 선언할 수 있는 문법으로 자바 14에서 도입됨

Sealed 인터페이스

코틀린 인터페이스를 선언할 떄 sealed 키워드를 추가할 수 있게 되었습니다. 이 인터페이스는 모든 구현체들을 컴파일 타임에 알 수 있습니다. (sealed 클래스와 동일)

sealed interface Polygon

fun draw(polygon: Polygon) = when (polygon) {
   is Rectangle -> // ...
   is Triangle -> // …
   // else is not needed - all possible implementations are covered
}

추가적으로 sealed 인터페이스도 일반적인 인터페이스와 마찬가지로, 클래스가 둘 이상의 sealed 인터페이스를 상속할 수 있어서 보다 유연하게 클래스 계층구조를 제한할 수 있습니다.

Package-wide sealed 클래스 상속

예전에는 sealed 클래스를 상속하는 서브클래스는 반드시 같은 파일내에 있어야 했습니다. 이제는 sealed 클래스를 상속하는 서브클래스가 같은 모듈(Same compilation unit) 내에 있고, 같은 패키지인 경우라면 다른파일이더라도 허용되도록 변경되었습니다. 이 서브클래스는 파일에서의 최상위 클래스이든 중첩 클래스이든 상관없이 가능합니다.

ㅇ단, 이 서브클래스는 로컬 or 익명 object 여서는 안됩니다. 반드시 적절한 클래스 이름을 가지고 있어야 합니다.

inline 클래스

inline 클래스는 오로지 값만을 가지고있는 value-based 클래스의 하위 개념입니다. inline class를 이용하면 객체를 생성하기 위해 메모리를 할당하는 오버헤드를 줄일 수 있는 장점이 있습니다.

inline 클래스는 class 키워드 앞에 value 키워드를 추가하여 정의합니다. Kotlin/JVM외에 Kotlin/JS, Kotlin/Native와의 호환성이 필요하다면 @JvmInline 어노테이션을 추가해줍니다.

@JvmInline
value class Password(val s: String)

inline 키워드는 이제 경고와 함꼐 deprecate 되었습니다.

value class? : 위의 예시에서 선언된 Password 클래스처럼 Primitive 값 하나를 래핑한 클래스로, 컴파일시점에 Value 클래스를 프로퍼티로 변경하도록 동작합니다. value 클래스의 자세한 설명은 공식문서에서 확인할 수 있습니다.

표준 라이브러리

unsigned integer 타입 지원

부호없는 정수 타입 (UIntULongUByteUShort)이 지원됩니다. 이 부호없는 정수타입은 다른 타입들과 동일하게 연산자, 범위 등을 사용할 수 있습니다. 아직 부호없는 배열은 Beta 단계입니다.

지역상관없는 대소문자 text API 지원

텍스트의 대/소문자 관련하여 지역에 구애받지 않는 새로운 API가 출시되었습니다. 이 API들은 기존에 지역의 영향을 받는 toLowerCase()toUpperCase()capitalize()decapitalize() API들을 대체할 수 있습니다.

  • String 클래스
이전 버전1.5.0 이후
String.toUpperCase()String.uppercase()
String.toLowerCase()String.lowercase()
String.capitalize()String.replaceFirstChar { it.uppercase() }
String.decapitalize()String.replaceFirstChar { it.lowercase() }
  • Char 클래스
이전 버전1.5.0 이후
Char.toUpperCase()Char.uppercaseChar(): Char
Char.uppercase(): String
Char.toLowerCase()Char.lowercaseChar(): Char
Char.lowercase(): String
Char.toTitleCase()Char.titlecaseChar(): Char
Char.titlecase(): String

Note : Kotlin/JVM에서는 명시적으로 Locale 파라미터를 지정할 수 있도록 uppercase()lowercase()titlecase() 메서드마다 오버로드된 메서드들이 존재합니다.

Char - Integer 변환 API 지원

코틀린 1.5.0부터 Char - Code / Char - Digit 변환 API를 제공합니다. 기존에 존재하는 String - Int 변환 메서드는 종종 혼동의 여지가 있었기에, 새로운 API를 통하여 기존것을 대체합니다. 새로운 API는 code 라고 명명하여 좀 더 명확하게 의미를 드러내도록 했습니다.

  • 정수코드를 통해 문자를 얻거나, 문자에서 정수코드를 얻는 메서드
fun Char(code: Int): Char
fun Char(code: UShort): Char
val Char.code: Int
  • 문자를 숫자값으로 변환하는 메서드
fun Char.digitToInt(radix: Int): Int
fun Char.digitToIntOrNull(radix: Int): Int?
  • 음수가 아닌 10 미만의 정수를 문자로 바꿔주는 Int 확장메서드
fun Int.digitToChar(radix: Int): Char

Number.toChar()Int.toChar()Char.toInt() 등 이전의 변환 API들은 deprecate 됩니다.

Path API 지원

java.nio.file.Path의 확장함수로 지원되는 Path API가 정식으로 지원됩니다.

// construct path with the div (/) operator
val baseDir = Path("/base")
val subDir = baseDir / "subdirectory"

// list files in a directory
val kotlinFiles: List<Path> = Path("/home/user").listDirectoryEntries("*.kt")

버림 나눗셈 (Floored division)과 나머지 연산

표준 라이브러리에 나머지 연산을 위한 새로운 연산자가 추가되었습니다.

  • floorDiv() : 함수는 버림나눗셈의 결과를 반환합니다. 정수타입에서만 이용가능합니다.
  • mod() 함수는 버림나눗셈의 나머지값을 반환합니다. 모든 숫자타입에서 이용가능합니다.

추가된 연산자는 기존의 정수 나눗셈이나 rem() 함수 (% 연산자와 동일)와 매우 유사해보이지만, 음수를 연산할 때 다르게 동작합니다.

  • a / b는 0에 가까운 값을 결과로 주는 반면에, a.floorDiv(b)는 더 작은 정수의 방향으로 반올림한 결과를 줍니다.
  • a.mod(b)의 결과값은 a와 a.floorDiv(b) * b 사이의 차이입니다. 이 값은 0이거나 b와 동일한 부호를 가지게 됩니다. 그러나 a % b는 다른 값을 가집니다.
// 예시
println("Floored division -5/3: ${(-5).floorDiv(3)}")
println( "Modulus: ${(-5).mod(3)}")

println("Truncated division -5/3: ${-5 / 3}")
println( "Remainder: ${-5 % 3}")

// 결과
Floored division -5/3: -2
Modulus: 1
Truncated division -5/3: -1
Remainder: -2

Duration API 변경

시간의 차이를 나타내기 위해서 Duration 클래스를 실험적으로 도입했었습니다. 코틀린 1.5.0에서 Duration API는 다음의 변경점이 있습니다.

  • internal value는 Double대신 Long을 이용하도록 변경되었습니다.
  • Long 타입으로 변환하는 새로운 API가 추가되었고, 기존의 Double타입의 API들은 Deprecate 되었습니다. 예를들어 Duration.inWholeMinutes는 Long타입으로 값을 반환하고, Duration.inMinutes로 대체되었습니다.
  • 숫자로부터 Duration을 생성하는 Companion 메서드가 추가되었습니다. 예를들어 Duration.seconds(Int)는 인자로 받은 숫자의 초 만큼을 나타내는 Duration 객체를 생성합니다. Int.seconds 같은 예전의 확장프로퍼티들은 deprecate 되었습니다.

멀티플랫폼 코드를 위한 Char 카테고리 확인 API 지원

코틀린 1.5.0에서는 멀티플랫폼 프로젝트에서 유니코드에 따라 문자의 카테고리를 얻는 API를 도입했습니다.

문자인지 숫자인지를 확인하는 함수는 다음과 같습니다.

  • Char.isDigit()
  • Char.isLetter()
  • Char.isLetterOrDigit()
val chars = listOf('a', '1', '+')
val (letterOrDigitList, notLetterOrDigitList) = chars.partition { it.isLetterOrDigit() }
println(letterOrDigitList) // [a, 1]
println(notLetterOrDigitList) // [+]

// 결과
[a, 1]
[+]

문자의 Case를 확인하는 함수는 다음과 같습니다.

  • Char.isLowerCase()
  • Char.isUpperCase()
  • Char.isTitleCase()
val chars = listOf('Dž', 'Lj', 'Nj', 'Dz', '1', 'A', 'a', '+')
val (titleCases, notTitleCases) = chars.partition { it.isTitleCase() }
println(titleCases) // [Dž, Lj, Nj, Dz]
println(notTitleCases) // [1, A, a, +]

// 결과
[Dž, Lj, Nj, Dz]
[1, A, a, +]

그 외에 다음 함수들도 추가되었습니다.

  • Char.isDefined()
  • Char.isISOControl()

Char.category 프로퍼티는 해당 문자의 카테고리를 의미하는 CharCategory enum class를 반환합니다.

새로운 콜렉션 함수 firstNotNullOf()

새로 추가된 firstNotNullOf()와 firstNotNullOfOrNull() 함수는 mapNotNull()과 first() / firstOrNull() 메서드를 조합한 것입니다. 새로운 함수는 원본 콜렉션을 커스텀 셀렉터 함수와 매핑하고, 최초의 non-null 값을 반환합니다. 해당하는 값이 없다면 firstNotNullOf()는 예외를 던지고, firstNotNullOfOrNull()은 null을 반환합니다.

val data = listOf("Kotlin", "1.5")
println(data.firstNotNullOf(String::toDoubleOrNull))
println(data.firstNotNullOfOrNull(String::toIntOrNull))

// 결과
1.5
null

String?.toBoolean()의 Strict 버전

이미 존재하는 String?.toBoolean()의 대소문자를 구분하는 엄격한 버전의 API가 추가되었습니다.

  • String.toBooleanStrict() : truefalse 리터럴을 제외한 다른 문자열이 있다면 예외를 던집니다.
  • String.toBooleanStrictOrNull() : truefalse 리터럴을 제외한 다른 문자열이 있다면 null을 반환합니다.
println("true".toBooleanStrict())
println("1".toBooleanStrictOrNull())
// println("1".toBooleanStrict()) // Exception

// 결과
true
null

원문

https://kotlinlang.org/docs/whatsnew15.html

댓글 없음:

댓글 쓰기