Kotlin 1.5.0의 새로운 기능
Kotlin 1.5.0의 새로운 기능
Kotlin 1.5.0은 새 언어 기능, 안정화된 IR 기반 JVM 컴파일러 백엔드, 성능 개선, 그리고 실험 기능 안정화와 구식 기능 deprecation 같은 진화적인 변화를 함께 가져왔어요. 2021년 5월 5일에 공개된 이 버전의 변경 사항 전체 개요는 릴리스 블로그 포스트에서도 확인할 수 있어요.
본문
언어 기능 (Language features)
Kotlin 1.5.0은 1.4.30에서 프리뷰로 소개된 새 언어 기능들의 안정 버전을 가져와요:
이 기능들에 대한 자세한 설명은 이 블로그 포스트와 Kotlin 문서의 해당 페이지에서 확인할 수 있어요.
JVM 레코드 지원
Java는 빠르게 진화하고 있어요. Kotlin이 Java와 계속 상호 운용되도록, Java의 최신 기능 중 하나인 record 클래스에 대한 지원을 도입했어요.
Kotlin의 JVM 레코드 지원은 양방향 상호 운용성을 제공해요:
- Kotlin 코드에서 Java record 클래스를 일반 프로퍼티 클래스처럼 사용할 수 있어요.
- Kotlin 클래스를 Java 코드에서 record로 쓰려면 그 클래스를
data클래스로 만들고@JvmRecord애노테이션을 붙이면 돼요.
@JvmRecord
data class User(val name: String, val age: Int)
Kotlin에서 JVM 레코드 사용하는 법 더 알아보기.
Sealed 인터페이스
Kotlin 인터페이스는 이제 sealed 한정자를 가질 수 있어요. 이 한정자는 인터페이스에서도 클래스에서처럼 동작해요. 즉 sealed 인터페이스의 모든 구현은 컴파일 시점에 알려져요.
sealed interface Polygon
이 점을 활용하면 예를 들어 완전한(exhaustive) when 표현식을 작성할 수 있어요.
fun draw(polygon: Polygon) = when (polygon) {
is Rectangle -> // ...
is Triangle -> // ...
// else는 필요 없다 - 가능한 모든 구현이 다 다뤄진다
}
추가로, sealed 인터페이스는 더 유연한 제한된 클래스 계층을 가능하게 해 줘요. 클래스가 둘 이상의 sealed 인터페이스를 직접 상속받을 수 있기 때문이에요.
class FilledRectangle: Polygon, Fillable
패키지 전체에 걸친 sealed 클래스 계층
이제 sealed 클래스는 같은 컴파일 단위와 같은 패키지의 모든 파일에 서브클래스를 가질 수 있어요. 이전에는 모든 서브클래스가 같은 파일에 있어야 했어요.
직접 서브클래스는 최상위에 있거나, 이름 있는 클래스·인터페이스·객체 안에 얼마든지 중첩될 수 있어요.
sealed 클래스의 서브클래스는 이름이 제대로 정규화된 것이어야 해요. 즉 로컬 객체나 익명 객체가 될 수는 없어요.
Inline 클래스
Inline 클래스는 값만 담는 값 기반(value-based) 클래스의 한 부류예요. 특정 타입의 값 하나를 감싸는 래퍼로, 메모리 할당으로 인한 추가 오버헤드 없이 사용할 수 있어요.
Inline 클래스는 클래스 이름 앞에 value 한정자를 붙여 선언할 수 있어요:
value class Password(val s: String)
JVM 백엔드에서는 특별한 @JvmInline 애노테이션도 필요해요:
@JvmInline
value class Password(val s: String)
inline 한정자는 이제 경고와 함께 deprecated 됐어요.
Kotlin/JVM
Kotlin/JVM은 내부적이든 사용자 대상이든 많은 개선을 받았어요. 그중 가장 눈에 띄는 것들은 다음과 같아요:
- 안정적인 JVM IR 백엔드
- 새 기본 JVM 타깃: 1.8
- invokedynamic을 통한 SAM 어댑터
- invokedynamic을 통한 람다
- @JvmDefault와 옛 Xjvm-default 모드 deprecation
- nullability 애노테이션 처리 개선
안정적인 JVM IR 백엔드
Kotlin/JVM 컴파일러의 IR 기반 백엔드가 이제 Stable이 되었고 기본으로 켜져요.
Kotlin 1.4.0부터 IR 기반 백엔드의 초기 버전이 프리뷰로 제공되었고, 이제 언어 버전 1.5에서 기본이 됐어요. 이전 언어 버전에서는 여전히 옛 백엔드가 기본으로 사용돼요.
IR 백엔드의 이점과 향후 개발에 대한 자세한 내용은 이 블로그 포스트에서 확인할 수 있어요.
Kotlin 1.5.0에서 옛 백엔드를 사용해야 한다면 프로젝트 설정 파일에 다음 내용을 추가하면 돼요:
- Gradle에서:
tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompile> {
kotlinOptions.useOldBackend = true
}
tasks.withType(org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompile) {
kotlinOptions.useOldBackend = true
}
- Maven에서:
<configuration>
<args>
<arg>-Xuse-old-backend</arg>
</args>
</configuration>
새 기본 JVM 타깃: 1.8
Kotlin/JVM 컴파일의 기본 타깃 버전이 이제 1.8이 됐어요. 1.6 타깃은 deprecated 됐어요.
JVM 1.6용 빌드가 필요하다면 여전히 그 타깃으로 전환할 수 있어요. 방법을 알아볼까요:
invokedynamic을 통한 SAM 어댑터
Kotlin 1.5.0은 이제 SAM(Single Abstract Method) 변환을 컴파일할 때 동적 호출(invokedynamic)을 사용해요:
- SAM 타입이 Java 인터페이스라면 모든 표현식에 대해
- SAM 타입이 Kotlin 함수형 인터페이스라면 람다에 대해
새 구현은 LambdaMetafactory.metafactory()를 사용하며, 컴파일 중 보조 래퍼 클래스가 더 이상 생성되지 않아요. 이는 애플리케이션 JAR 크기를 줄여 JVM 시작 성능을 개선해요.
옛 익명 클래스 생성 방식으로 되돌리려면 컴파일러 옵션 -Xsam-conversions=class를 추가하면 돼요.
Gradle, Maven, 커맨드라인 컴파일러에서 컴파일러 옵션을 추가하는 방법을 알아보세요.
invokedynamic을 통한 람다
Kotlin 1.5.0은 (함수형 인터페이스 인스턴스로 변환되지 않는) 일반 Kotlin 람다를 동적 호출(invokedynamic)로 컴파일하는 실험적 지원을 도입해요. 이 구현은 LambdaMetafactory.metafactory()를 사용해서 필요한 클래스를 런타임에 효과적으로 생성하므로, 더 가벼운 바이너리를 만들어요. 현재 일반 람다 컴파일과 비교해 세 가지 제한이 있어요:
invokedynamic으로 컴파일된 람다는 직렬화할 수 없어요.- 그런 람다에서
toString()을 호출하면 덜 읽기 쉬운 문자열 표현이 생겨요. - 실험적 reflect API는
LambdaMetafactory로 만든 람다를 지원하지 않아요.
이 기능을 시도해 보려면 -Xlambdas=indy 컴파일러 옵션을 추가하면 돼요. 이에 대한 의견은 이 YouTrack 티켓으로 공유해 주시면 감사하겠어요.
Gradle, Maven, 커맨드라인 컴파일러에서 컴파일러 옵션을 추가하는 방법을 알아보세요.
@JvmDefault와 옛 Xjvm-default 모드 deprecation
Kotlin 1.4.0 이전에는 @JvmDefault 애노테이션과 -Xjvm-default=enable, -Xjvm-default=compatibility 모드가 있었어요. 이들은 Kotlin 인터페이스의 특정 비추상 멤버에 대해 JVM 기본 메서드를 만드는 데 사용됐어요.
Kotlin 1.4.0에서 우리는 새 Xjvm-default 모드를 도입했어요. 이는 프로젝트 전체에 대해 기본 메서드 생성을 켜는 방식이에요.
Kotlin 1.5.0에서는 @JvmDefault와 옛 Xjvm-default 모드(-Xjvm-default=enable, -Xjvm-default=compatibility)를 deprecated 해요.
nullability 애노테이션 처리 개선
Kotlin은 nullability 애노테이션으로 Java의 타입 nullability 정보를 처리하는 것을 지원해요. Kotlin 1.5.0은 이 기능에 몇 가지 개선을 도입해요:
- 의존성으로 사용되는 컴파일된 Java 라이브러리의 타입 인자에서 nullability 애노테이션을 읽어요.
TYPE_USE타깃을 가진 nullability 애노테이션을 다음에 대해 지원해요:- 배열(Array)
- 가변 인자(Varargs)
- 필드(Fields)
- 타입 파라미터와 그 경계(bound)
- 기본 클래스와 인터페이스의 타입 인자
- nullability 애노테이션이 타입에 적용 가능한 여러 타깃을 갖고 있고 그중 하나가
TYPE_USE라면TYPE_USE가 우선돼요. 예를 들어@Nullable이TYPE_USE와METHOD타깃을 모두 지원한다면, 메서드 시그니처@Nullable String[] f()는fun f(): Array<String?>!이 돼요.
이렇게 새로 지원되는 경우에 대해 Kotlin에서 Java를 호출할 때 타입 nullability를 잘못 쓰면 경고가 발생해요. 이런 경우에 대해 엄격 모드(오류 보고 포함)를 켜려면 -Xtype-enhancement-improvements-strict-mode 컴파일러 옵션을 사용하면 돼요.
Kotlin/Native
Kotlin/Native는 이제 더 성능이 좋고 안정적이에요. 주목할 만한 변경 사항은 다음과 같아요:
성능 개선
1.5.0에서 Kotlin/Native는 컴파일과 실행을 모두 빠르게 하는 일련의 성능 개선을 받아요.
컴파일러 캐시가 이제 linuxX64(Linux 호스트에서만)와 iosArm64 타깃의 디버그 모드에서 지원돼요. 컴파일러 캐시를 켜면 첫 컴파일을 제외하고 대부분의 디버그 컴파일이 훨씬 빨라져요. 테스트 프로젝트에서 약 200%의 속도 향상을 측정했어요.
새 타깃에 컴파일러 캐시를 사용하려면 프로젝트의 gradle.properties에 다음 줄을 추가해 옵트인하면 돼요:
linuxX64용:kotlin.native.cacheKind.linuxX64=staticiosArm64용:kotlin.native.cacheKind.iosArm64=static
컴파일러 캐시를 켠 뒤 문제가 생기면 이슈 트래커 YouTrack으로 보고해 주세요.
다른 개선 사항들은 Kotlin/Native 코드의 실행을 빨라지게 해요:
- 사소한(trivial) 프로퍼티 접근자가 인라인돼요.
- 문자열 리터럴에 대한
trimIndent()가 컴파일 중에 평가돼요.
메모리 누수 검사기 비활성화
기본 제공 Kotlin/Native 메모리 누수 검사기가 기본적으로 꺼졌어요.
이 검사기는 처음에 내부용으로 설계됐고, 누수를 모든 경우가 아니라 제한된 경우에만 찾을 수 있어요. 게다가 나중에 애플리케이션 크래시를 일으킬 수 있는 문제가 있다는 것이 드러났어요. 그래서 메모리 누수 검사기를 끄기로 했어요.
메모리 누수 검사기는 단위 테스트 같은 특정 경우에는 여전히 유용할 수 있어요. 그런 경우 다음 코드를 추가해 켤 수 있어요:
Platform.isMemoryLeakCheckerActive = true
애플리케이션 런타임에서 검사기를 켜는 것은 권장하지 않는다는 점을 명심하세요.
Kotlin/JS
Kotlin/JS는 1.5.0에서 진화적인 변화를 받아요. JS IR 컴파일러 백엔드를 안정화하는 쪽으로 작업을 계속하고 있으며 다른 업데이트도 함께 제공하고 있어요:
webpack 5로 업그레이드
Kotlin/JS Gradle 플러그인은 이제 브라우저 타깃에 webpack 4 대신 webpack 5를 사용해요. 이는 호환되지 않는 변화를 가져오는 주요 webpack 업그레이드예요. 커스텀 webpack 구성을 사용하고 있다면 webpack 5 릴리스 노트를 꼭 확인해 주세요.
webpack으로 Kotlin/JS 프로젝트 번들링 더 알아보기.
IR 컴파일러용 프레임워크와 라이브러리
Kotlin/JS 컴파일러의 IR 기반 백엔드 작업과 함께, 라이브러리 작성자들이 both 모드로 프로젝트를 빌드하도록 장려하고 도와주고 있어요. 이는 두 Kotlin/JS 컴파일러 모두에 대한 아티팩트를 만들 수 있다는 뜻이고, 그래서 새 컴파일러를 위한 생태계가 커져요.
이미 많은 유명한 프레임워크와 라이브러리가 IR 백엔드에서 사용 가능해요: KVision, fritz2, doodle 등이 있어요. 프로젝트에서 이들을 사용하고 있다면 이미 IR 백엔드로 빌드하고 그 이점을 볼 수 있어요.
직접 라이브러리를 만든다면 both 모드로 컴파일해서 여러분의 클라이언트도 새 컴파일러와 함께 쓸 수 있게 해 주세요.
Kotlin Multiplatform
Kotlin 1.5.0에서는 각 플랫폼별 테스트 의존성 선택이 간단해졌고, 이제 Gradle 플러그인이 자동으로 처리해요.
문자 카테고리를 얻는 새 API가 멀티플랫폼 프로젝트에서 사용 가능해졌어요.
표준 라이브러리
표준 라이브러리는 실험 부분 안정화부터 새 기능 추가까지 다양한 변경과 개선을 받았어요:
- 안정적인 부호 없는 정수 타입
- 대문자/소문자 텍스트를 위한 안정적인 로케일 무관 API
- 안정적인 Char-to-integer 변환 API
- 안정적인 Path API
- 바닥 나눗셈(floored division)과 mod 연산자
- Duration API 변경
- 멀티플랫폼 코드에서 문자 카테고리를 얻는 새 API
- 새 컬렉션 함수 firstNotNullOf()
- String?.toBoolean()의 엄격 버전
표준 라이브러리 변경 사항에 대해 더 알고 싶다면 이 블로그 포스트에서 확인할 수 있어요.
안정적인 부호 없는 정수 타입
UInt, ULong, UByte, UShort 부호 없는 정수 타입이 이제 Stable이 됐어요. 이 타입들에 대한 연산, 범위, 진행(progression)도 마찬가지예요. 부호 없는 배열과 그 연산은 Beta에 남아 있어요.
대문자/소문자 텍스트를 위한 안정적인 로케일 무관 API
이 릴리스는 대문자/소문자 텍스트 변환을 위한 새 로케일 무관 API를 가져와요. 이것은 로케일에 민감한 toLowerCase(), toUpperCase(), capitalize(), decapitalize() API 함수들의 대안을 제공해요. 새 API는 로케일 설정 차이로 인한 오류를 피하는 데 도움을 줘요.
Kotlin 1.5.0은 다음과 같은 완전히 Stable한 대안들을 제공해요:
-
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(): StringChar.toLowerCase()Char.lowercaseChar(): Char/Char.lowercase(): StringChar.toTitleCase()Char.titlecaseChar(): Char/Char.titlecase(): String
옛 API 함수들은 deprecated로 표시되고 향후 릴리스에서 제거될 거예요.
텍스트 처리 함수 변경의 전체 목록은 KEEP에서 확인할 수 있어요.
안정적인 char-to-integer 변환 API
Kotlin 1.5.0부터 새 char-to-code 및 char-to-digit 변환 함수가 Stable이 됐어요. 이 함수들은 흔히 문자열-to-Int 변환과 혼동되던 기존 API 함수들을 대체해요.
새 API는 이런 이름 혼동을 없애서 코드 동작을 더 투명하고 모호하지 않게 만들어요.
이 릴리스는 이름이 명확한 다음 함수 집합들로 나뉜 Char 변환을 도입해요:
Char의 정수 코드를 얻고, 주어진 코드로Char를 만드는 함수:
fun Char(code: Int): Char
fun Char(code: UShort): Char
val Char.code: Int
Char를 그것이 나타내는 숫자의 숫자 값으로 변환하는 함수:
fun Char.digitToInt(radix: Int): Int
fun Char.digitToIntOrNull(radix: Int): Int?
- 음수 아닌 한 자리 숫자를 그에 해당하는
Char표현으로 변환하는Int확장 함수:
fun Int.digitToChar(radix: Int): Char
Number.toChar()(Int.toChar() 제외)와 그 구현들, 그리고 Char.toInt() 같은 숫자 타입으로의 변환용 Char 확장을 포함한 옛 변환 API들은 이제 deprecated 됐어요.
char-to-integer 변환 API를 KEEP에서 더 알아보기.
안정적인 Path API
java.nio.file.Path를 위한 확장이 있는 실험적 Path API가 이제 Stable이 됐어요.
// div (/) 연산자로 경로 구성하기
val baseDir = Path("/base")
val subDir = baseDir / "subdirectory"
// 디렉터리에서 파일 목록 나열하기
val kotlinFiles: List<Path> = Path("/home/user").listDirectoryEntries("*.kt")
바닥 나눗셈(floored division)과 mod 연산자
표준 라이브러리에 모듈러 산술을 위한 새 연산이 추가됐어요:
floorDiv()는 바닥 나눗셈의 결과를 반환해요. 정수 타입에서 사용할 수 있어요.mod()는 바닥 나눗셈의 나머지(계수)를 반환해요. 모든 숫자 타입에서 사용할 수 있어요.
이 연산들은 기존의 정수 나눗셈과 rem() 함수(또는 % 연산자)와 아주 비슷해 보이지만, 음수에서는 다르게 동작해요:
a.floorDiv(b)는 일반/와 달라요.floorDiv는 결과를 (더 작은 정수 쪽으로) 내림하지만,/는 0에 가까운 정수로 버림해요.a.mod(b)는a와a.floorDiv(b) * b의 차이예요. 0이거나b와 같은 부호를 갖지만,a % b는 다른 부호를 가질 수 있어요.
fun main() {
//sampleStart
println("Floored division -5/3: ${(-5).floorDiv(3)}")
println( "Modulus: ${(-5).mod(3)}")
println("Truncated division -5/3: ${-5 / 3}")
println( "Remainder: ${-5 % 3}")
//sampleEnd
}
Duration API 변경
다른 시간 단위로 지속 시간을 나타내기 위한 실험적 Duration 클래스가 있어요. 1.5.0에서 Duration API는 다음 변경을 받았어요:
- 이제 내부 값 표현이
Double대신Long을 사용해서 더 나은 정밀도를 제공해요. - 특정 시간 단위로
Long으로 변환하는 새 API가 있어요. 이것은Double값으로 동작하던 옛 API를 대체하며, 옛 API는 이제 deprecated 됐어요. 예를 들어 Duration.inWholeMinutes는 지속 시간 값을Long으로 표현해 반환하고Duration.inMinutes를 대체해요. - 숫자에서
Duration을 만드는 새 동반(companion) 함수가 있어요. 예를 들어 Duration.seconds(Int)는 정수 초를 나타내는Duration객체를 만들어요.Int.seconds같은 옛 확장 프로퍼티는 이제 deprecated 됐어요.
import kotlin.time.Duration
import kotlin.time.ExperimentalTime
@ExperimentalTime
fun main() {
//sampleStart
val duration = Duration.milliseconds(120000)
println("There are ${duration.inWholeSeconds} seconds in ${duration.inWholeMinutes} minutes")
//sampleEnd
}
멀티플랫폼 코드에서 문자 카테고리를 얻는 새 API
Kotlin 1.5.0은 멀티플랫폼 프로젝트에서 Unicode에 따른 문자의 카테고리를 얻는 새 API를 도입해요. 이제 여러 함수가 모든 플랫폼과 공통 코드에서 사용 가능해요.
문자가 문자(letter)인지 숫자(digit)인지 확인하는 함수:
fun main() {
//sampleStart
val chars = listOf('a', '1', '+')
val (letterOrDigitList, notLetterOrDigitList) = chars.partition { it.isLetterOrDigit() }
println(letterOrDigitList) // [a, 1]
println(notLetterOrDigitList) // [+]
//sampleEnd
}
문자의 대소문자 여부를 확인하는 함수:
fun main() {
//sampleStart
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, +]
//sampleEnd
}
그 외 함수들:
Unicode에 따른 문자의 일반 카테고리를 나타내는 프로퍼티 Char.category와 그것의 반환 타입 enum 클래스 CharCategory도 이제 멀티플랫폼 프로젝트에서 사용 가능해요.
새 컬렉션 함수 firstNotNullOf()
새 firstNotNullOf()와 firstNotNullOfOrNull() 함수는 mapNotNull()을 first() 또는 firstOrNull()과 결합해요. 원본 컬렉션을 커스텀 선택자(selector) 함수로 매핑하고 첫 번째 non-null 값을 반환해요. 그런 값이 없다면 firstNotNullOf()는 예외를 던지고, firstNotNullOfOrNull()은 null을 반환해요.
fun main() {
//sampleStart
val data = listOf("Kotlin", "1.5")
println(data.firstNotNullOf(String::toDoubleOrNull))
println(data.firstNotNullOfOrNull(String::toIntOrNull))
//sampleEnd
}
String?.toBoolean()의 엄격 버전
기존 String?.toBoolean()의 대소문자 구분 엄격 버전을 도입하는 두 개의 새 함수가 있어요:
- String.toBooleanStrict()는 리터럴
true와false를 제외한 모든 입력에 대해 예외를 던져요. - String.toBooleanStrictOrNull()은 리터럴
true와false를 제외한 모든 입력에 대해 null을 반환해요.
fun main() {
//sampleStart
println("true".toBooleanStrict())
println("1".toBooleanStrictOrNull())
// println("1".toBooleanStrict()) // 예외 발생
//sampleEnd
}
kotlin-test 라이브러리
kotlin-test 라이브러리는 몇 가지 새 기능을 도입해요:
멀티플랫폼 프로젝트에서 간단해진 테스트 의존성 사용
이제 commonTest 소스셋에 테스트용 의존성을 추가할 때 kotlin-test 의존성을 사용하면, Gradle 플러그인이 각 테스트 소스셋에 해당하는 플랫폼 의존성을 추론해요:
- JVM 소스셋용
kotlin-test-junit(아래의 Kotlin/JVM 소스셋용 테스팅 프레임워크 자동 선택 참고) - Kotlin/JS 소스셋용
kotlin-test-js - 공통 소스셋용
kotlin-test-common과kotlin-test-annotations-common - Kotlin/Native 소스셋용 추가 아티팩트 없음
추가로, 공유되거나 플랫폼 특화된 소스셋 어디에서든 kotlin-test 의존성을 사용할 수 있어요.
명시적 의존성을 가진 기존 kotlin-test 설정은 Gradle과 Maven 모두에서 계속 동작해요.
Kotlin/JVM 소스셋용 테스팅 프레임워크 자동 선택
Gradle 플러그인이 이제 테스팅 프레임워크에 대한 의존성을 자동으로 선택하고 추가해요. 여러분이 할 일은 공통 소스셋에 kotlin-test 의존성을 추가하는 것뿐이에요.
Gradle은 기본적으로 JUnit 4를 사용해요. 따라서 kotlin("test") 의존성은 JUnit 4용 변형, 즉 kotlin-test-junit으로 해석돼요:
kotlin {
sourceSets {
val commonTest by getting {
dependencies {
implementation(kotlin("test")) // 이 줄이 의존성을 가져옴
// JUnit 4를 전이적으로
}
}
}
}
kotlin {
sourceSets {
commonTest {
dependencies {
implementation kotlin("test") // 이 줄이 의존성을 가져옴
// JUnit 4를 전이적으로
}
}
}
}
테스트 태스크에서 useJUnitPlatform() 또는 useTestNG()를 호출해 JUnit 5나 TestNG를 선택할 수 있어요:
tasks {
test {
// TestNG 지원 켜기
useTestNG()
// 또는
// JUnit Platform (일명 JUnit 5) 지원 켜기
useJUnitPlatform()
}
}
자동 테스팅 프레임워크 선택을 끄려면 프로젝트의 gradle.properties에 kotlin.test.infer.jvm.variant=false 줄을 추가하면 돼요.
단언(assertion) 함수 업데이트
이 릴리스는 새 단언 함수를 가져오고 기존 함수를 개선해요.
kotlin-test 라이브러리는 이제 다음 기능을 가져요:
-
값의 타입 확인 — 새
assertIs<T>와assertIsNot<T>로 값의 타입을 확인할 수 있어요:@Test fun testFunction() { val s: Any = "test" assertIs<String>(s) // 단언이 실패하면 s의 실제 타입을 언급하는 AssertionError를 던진다 // assertIs 안의 contract 덕분에 이제 s.length를 출력할 수 있다 println("${s.length}") }타입 erasure 때문에 이 단언 함수는 다음 예시에서
value가List타입인지만 확인하고, 특정String요소 타입의 리스트인지는 확인하지 않아요:assertIs<List<String>>(value). -
배열, 시퀀스, 임의 iterable에 대한 컨테이너 내용 비교 — 구조적 동등성을 구현하지 않는 서로 다른 컬렉션들의 내용을 비교하는 새 오버로드된
assertContentEquals()함수 집합이 있어요:@Test fun test() { val expectedArray = arrayOf(1, 2, 3) val actualArray = Array(3) { it + 1 } assertContentEquals(expectedArray, actualArray) } -
Double과Float숫자용assertEquals()와assertNotEquals()의 새 오버로드 —assertEquals()함수에 두Double또는Float숫자를 절대 정밀도로 비교할 수 있게 하는 새 오버로드가 있어요. 정밀도 값은 함수의 세 번째 파라미터로 지정해요:@Test fun test() { val x = sin(PI) // 정밀도 파라미터 val tolerance = 0.000001 assertEquals(0.0, x, tolerance) } -
컬렉션과 요소의 내용 확인용 새 함수 —
assertContains()함수로 컬렉션이나 요소가 무언가를 포함하는지 확인할 수 있어요.IntRange,String등contains()연산자를 가진 Kotlin 컬렉션과 요소에 사용할 수 있어요:@Test fun test() { val sampleList = listOf<String>("sample", "sample2") val sampleString = "sample" assertContains(sampleList, sampleString) // 컬렉션 안의 요소 assertContains(sampleString, "amp") // 문자열 안의 부분 문자열 } -
assertTrue(),assertFalse(),expect()함수가 이제 inline — 이제 이것들을 inline 함수로 사용할 수 있어서, 람다 표현식 안에서 suspend 함수를 호출할 수 있어요:@Test fun test() = runBlocking<Unit> { val deferred = async { "Kotlin is nice" } assertTrue("Kotlin substring should be present") { deferred.await() .contains("Kotlin") } }
kotlinx 라이브러리
Kotlin 1.5.0과 함께 kotlinx 라이브러리의 새 버전들도 공개됐어요:
Coroutines 1.5.0-RC
kotlinx.coroutines 1.5.0-RC는 다음을 담고 있어요:
- 새 channels API
- 안정적인 reactive 통합
- 그리고 더
Kotlin 1.5.0부터 실험적 coroutines은 비활성화되고 -Xcoroutines=experimental 플래그는 더 이상 지원되지 않아요.
changelog와 kotlinx.coroutines 1.5.0 릴리스 블로그 포스트에서 더 알아보세요.
Serialization 1.2.1
kotlinx.serialization 1.2.1은 다음을 담고 있어요:
- JSON 직렬화 성능 개선
- JSON 직렬화에서 여러 이름 지원
@Serializable클래스에서 실험적 .proto 스키마 생성- 그리고 더
changelog와 kotlinx.serialization 1.2.1 릴리스 블로그 포스트에서 더 알아보세요.
dateTime 0.2.0
kotlinx-datetime 0.2.0은 다음을 담고 있어요:
@SerializableDatetime 객체DateTimePeriod와DatePeriod의 정규화된 API- 그리고 더
changelog와 kotlinx-datetime 0.2.0 릴리스 블로그 포스트에서 더 알아보세요.
Kotlin 1.5.0으로 마이그레이션
IntelliJ IDEA와 Android Studio는 Kotlin 플러그인 1.5.0이 사용 가능해지면 업데이트를 제안할 거예요.
기존 프로젝트를 Kotlin 1.5.0으로 마이그레이션하려면 Kotlin 버전을 1.5.0으로 바꾸고 Gradle 또는 Maven 프로젝트를 다시 임포트하면 돼요. Kotlin 1.5.0으로 업데이트하는 방법 알아보기.
Kotlin 1.5.0으로 새 프로젝트를 시작하려면 Kotlin 플러그인을 업데이트하고 File | New | Project에서 Project Wizard를 실행하면 돼요.
새 커맨드라인 컴파일러는 GitHub 릴리스 페이지에서 다운로드할 수 있어요.
Kotlin 1.5.0은 기능 릴리스이므로 언어에 호환되지 않는 변화를 가져올 수 있어요. 그런 변화의 자세한 목록은 Kotlin 1.5 호환성 가이드에서 확인할 수 있어요.