Kotlin 1.5.20의 새로운 기능
Kotlin 1.5.20의 새로운 기능
Kotlin 1.5.20은 1.5.0의 새 기능에서 발견된 문제들을 고치고, 다양한 툴링 개선도 함께 담고 있어요. 2021년 6월 24일에 공개된 이 버전의 변경 사항 개요는 릴리스 블로그 포스트와 아래 영상에서도 확인할 수 있어요.
본문
Kotlin/JVM
Kotlin 1.5.20은 JVM 플랫폼에서 다음 업데이트를 받아요:
- invokedynamic을 통한 문자열 연결
- JSpecify nullness 애노테이션 지원
- Kotlin과 Java 코드가 함께 있는 모듈에서 Java의 Lombok 생성 메서드 호출 지원
invokedynamic을 통한 문자열 연결
Kotlin 1.5.20은 JVM 9+ 타깃에서 문자열 연결을 동적 호출(invokedynamic)로 컴파일해요. 이는 최신 Java 버전에 맞춰가는 것이에요. 더 정확히는 문자열 연결에 StringConcatFactory.makeConcatWithConstants()를 사용해요.
이전 버전에서 사용하던 StringBuilder.append() 방식으로 되돌리려면 컴파일러 옵션 -Xstring-concat=inline을 추가하면 돼요.
Gradle, Maven, 커맨드라인 컴파일러에서 컴파일러 옵션을 추가하는 방법을 알아보세요.
JSpecify nullness 애노테이션 지원
Kotlin 컴파일러는 Java에서 Kotlin으로 nullability 정보를 전달하기 위해 다양한 종류의 nullability 애노테이션을 읽을 수 있어요. 버전 1.5.20은 표준화된 통합 Java nullness 애노테이션 집합을 포함하는 JSpecify 프로젝트에 대한 지원을 도입해요.
JSpecify를 사용하면 null-safety가 Java와 상호 운용되도록 돕는 더 상세한 nullability 정보를 제공할 수 있어요. 선언, 패키지, 모듈 범위에 대해 기본 nullability를 설정하고, 파라미터릭 nullability를 지정하는 등의 일을 할 수 있어요. 자세한 내용은 JSpecify 사용자 가이드에서 확인할 수 있어요.
Kotlin이 JSpecify 애노테이션을 처리하는 예시를 볼까요:
// JavaClass.java
import org.jspecify.nullness.*;
@NullMarked
public class JavaClass {
public String notNullableString() { return ""; }
public @Nullable String nullableString() { return ""; }
}
// Test.kt
fun kotlinFun() = with(JavaClass()) {
notNullableString().length // OK
nullableString().length // Warning: receiver nullability mismatch
}
1.5.20에서는 JSpecify가 제공하는 nullability 정보에 따른 모든 불일치가 경고로 보고돼요. JSpecify로 작업할 때 엄격 모드(오류 보고 포함)를 켜려면 -Xjspecify-annotations=strict와 -Xtype-enhancement-improvements-strict-mode 컴파일러 옵션을 사용하면 돼요. JSpecify 프로젝트는 활발히 개발 중이라는 점을 유의하세요. 그 API와 구현은 언제든 크게 바뀔 수 있어요.
Kotlin과 Java 코드가 함께 있는 모듈에서 Java의 Lombok 생성 메서드 호출 지원
Lombok 컴파일러 플러그인은 Experimental 상태예요. 언제든 삭제되거나 바뀔 수 있어요. 평가 목적으로만 사용해야 해요. 이에 대한 의견은 YouTrack에서 확인할 수 있어요.
Kotlin 1.5.20은 실험적인 Lombok 컴파일러 플러그인을 도입해요. 이 플러그인은 Kotlin과 Java 코드가 함께 있는 모듈 안에서 Java의 Lombok 선언을 생성하고 사용할 수 있게 해 줘요. Lombok 애노테이션은 Java 소스에서만 동작하고, Kotlin 코드에서 사용하면 무시돼요.
이 플러그인은 다음 애노테이션들을 지원해요:
@Getter,@Setter@NoArgsConstructor,@RequiredArgsConstructor,@AllArgsConstructor@Data@With@Value
이 플러그인에 대한 작업은 계속 진행 중이에요. 현재의 자세한 상태를 알아보려면 Lombok 컴파일러 플러그인 README를 방문해 주세요.
현재 @Builder 애노테이션을 지원할 계획은 없어요. 그러나 YouTrack에서 @Builder에 투표하면 이를 고려할 수 있어요.
Kotlin/Native
Kotlin/Native 1.5.20은 새 기능의 프리뷰와 툴링 개선을 제공해요:
생성된 Objective-C 헤더로 KDoc 주석 옵트인 내보내기
생성된 Objective-C 헤더로 KDoc 주석을 내보내는 기능은 Experimental 상태예요. 언제든 삭제되거나 바뀔 수 있어요.
이제 Kotlin/Native 컴파일러를 설정해서 Kotlin 코드의 문서 주석(KDoc)을 그로부터 생성된 Objective-C 프레임워크로 내보낼 수 있어요. 그렇게 하면 프레임워크 소비자에게도 그 주석이 보여요.
예를 들어 KDoc이 있는 다음 Kotlin 코드는:
/**
* Prints the sum of the arguments.
* Properly handles the case when the sum doesn't fit in 32-bit integer.
*/
fun printSum(a: Int, b: Int) = println(a.toLong() + b)
다음과 같은 Objective-C 헤더를 만들어요:
/**
* Prints the sum of the arguments.
* Properly handles the case when the sum doesn't fit in 32-bit integer.
*/
+ (void)printSumA:(int32_t)a b:(int32_t)b __attribute__((swift_name("printSum(a:b:)")));
이것은 Swift에서도 잘 동작해요.
KDoc 주석을 Objective-C 헤더로 내보내는 기능을 시도해 보려면 -Xexport-kdoc 컴파일러 옵션을 사용하면 돼요. 주석을 내보내려는 Gradle 프로젝트의 build.gradle(.kts)에 다음 줄을 추가해 주세요:
kotlin {
targets.withType<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget> {
compilations.get("main").kotlinOptions.freeCompilerArgs += "-Xexport-kdoc"
}
}
kotlin {
targets.withType(org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget) {
compilations.get("main").kotlinOptions.freeCompilerArgs += "-Xexport-kdoc"
}
}
의견은 이 YouTrack 티켓으로 공유해 주시면 감사하겠어요.
컴파일러 버그 수정
Kotlin/Native 컴파일러는 1.5.20에서 여러 버그 수정을 받았어요. 전체 목록은 changelog에서 확인할 수 있어요.
호환성에 영향을 주는 중요한 버그 수정이 하나 있어요. 이전 버전에서는 잘못된 UTF 서로게이트 쌍을 포함한 문자열 상수가 컴파일 중 값을 잃었는데, 이제 그런 값이 보존돼요. 애플리케이션 개발자는 안심하고 1.5.20으로 업데이트할 수 있어요. 아무것도 깨지지 않아요. 그러나 1.5.20으로 컴파일된 라이브러리는 이전 컴파일러 버전과 호환되지 않아요. 자세한 내용은 이 YouTrack 이슈를 참고하세요.
하나의 배열 안에서 Array.copyInto() 성능 개선
Array.copyInto()가 소스와 대상이 같은 배열일 때 동작하는 방식을 개선했어요. 이제 이런 연산은 (복사하는 객체 수에 따라) 최대 20배까지 빨라져요. 이 사용 사례에 대한 메모리 관리 최적화 덕분이에요.
Kotlin/JS
1.5.20과 함께, Kotlin/JS의 새 IR 기반 백엔드로 프로젝트를 마이그레이션하는 데 도움이 되는 가이드를 공개했어요.
JS IR 백엔드용 마이그레이션 가이드
새 JS IR 백엔드용 마이그레이션 가이드는 마이그레이션 중 만날 수 있는 문제들을 식별하고 그에 대한 해결책을 제공해요. 가이드에서 다루지 않는 문제를 발견하면 이슈 트래커로 보고해 주세요.
Gradle
Kotlin 1.5.20은 Gradle 경험을 개선할 수 있는 다음 기능들을 도입해요:
kapt에서 애노테이션 프로세서 클래스로더 캐싱
이제 kapt에서 애노테이션 프로세서의 클래스로더를 캐시할 수 있게 하는 새 실험적 기능이 있어요. 이 기능은 연속된 Gradle 실행에서 kapt의 속도를 높여줘요.
이 기능을 켜려면 gradle.properties 파일에서 다음 프로퍼티들을 사용하면 돼요:
# 양수 값이 캐싱을 켠다
# kapt를 사용하는 모듈 수와 같은 값을 사용한다
kapt.classloaders.cache.size=5
# 캐싱이 동작하도록 끈다
kapt.include.compile.classpath=false
kapt에 대해 더 알아보세요.
kotlin.parallel.tasks.in.project 빌드 프로퍼티 deprecation
이 릴리스부터 Kotlin 병렬 컴파일은 Gradle 병렬 실행 플래그 --parallel로 제어돼요. 이 플래그를 사용하면 Gradle이 태스크를 동시에 실행해서 컴파일 태스크의 속도를 높이고 리소스를 더 효율적으로 사용해요.
더 이상 kotlin.parallel.tasks.in.project 프로퍼티를 사용할 필요가 없어요. 이 프로퍼티는 deprecated 됐고 다음 주요 릴리스에서 제거될 거예요.
표준 라이브러리
Kotlin 1.5.20은 문자를 다루는 여러 함수의 플랫폼별 구현을 바꿔서 플랫폼 간 통일을 가져왔어요:
- Kotlin/Native와 Kotlin/JS에서 Char.digitToInt()가 모든 Unicode 숫자 지원.
- 플랫폼 간 Char.isLowerCase()/isUpperCase() 구현 통일.
Kotlin/Native와 Kotlin/JS에서 Char.digitToInt()가 모든 Unicode 숫자 지원
Char.digitToInt()는 문자가 나타내는 십진 숫자의 숫자 값을 반환해요. 1.5.20 이전에는 이 함수가 모든 Unicode 숫자 문자를 지원하는 경우가 Kotlin/JVM뿐이었어요. Native와 JS 플랫폼의 구현은 ASCII 숫자만 지원했어요.
이제부터 Kotlin/Native와 Kotlin/JS 모두에서 어떤 Unicode 숫자 문자에든 Char.digitToInt()를 호출해서 그 숫자 표현을 얻을 수 있어요.
fun main() {
//sampleStart
val ten = '\u0661'.digitToInt() + '\u0039'.digitToInt() // ARABIC-INDIC DIGIT ONE + DIGIT NINE
println(ten)
//sampleEnd
}
플랫폼 간 Char.isLowerCase()/isUpperCase() 구현 통일
Char.isUpperCase()와 Char.isLowerCase() 함수는 문자의 대소문자에 따라 불리언 값을 반환해요. Kotlin/JVM에서 그 구현은 General_Category와 Other_Uppercase/Other_Lowercase Unicode 프로퍼티를 모두 확인해요.
1.5.20 이전에는 다른 플랫폼의 구현이 다르게 동작해서 일반 카테고리만 고려했어요. 1.5.20에서는 구현이 플랫폼 간에 통일되어 문자 대소문자를 판정할 때 두 프로퍼티를 모두 사용해요:
fun main() {
//sampleStart
val latinCapitalA = 'A' // has "Lu" general category
val circledLatinCapitalA = 'Ⓐ' // has "Other_Uppercase" property
println(latinCapitalA.isUpperCase() && circledLatinCapitalA.isUpperCase())
//sampleEnd
}