Kotlin 1.5.30의 새로운 기능

Kotlin 1.5.30의 새로운 기능

Kotlin 1.5.30은 향후 변화의 프리뷰를 포함한 언어 업데이트, 플랫폼 지원과 툴링의 다양한 개선, 그리고 새 표준 라이브러리 함수를 제공해요. 2021년 8월 24일에 공개된 이 버전의 주요 개선 사항은 다음과 같아요:

  • 실험적 sealed when 문, opt-in 요구 사용의 변화 등 언어 기능
  • Apple silicon에 대한 네이티브 지원
  • Kotlin/JS IR 백엔드가 Beta 도달
  • 개선된 Gradle 플러그인 경험

변경 사항에 대한 짧은 개요는 릴리스 블로그 포스트와 아래 영상에서도 확인할 수 있어요.

출처: What's new in Kotlin 1.5.30

본문

언어 기능 (Language features)

Kotlin 1.5.30은 향후 언어 변화의 프리뷰를 보여주고, opt-in 요구 메커니즘과 타입 추론에 개선을 가져와요:

sealed 및 Boolean 주제에 대한 완전한 when 문

완전한(exhaustive) when 문은 그 주제의 가능한 모든 타입이나 값에 대한 분기, 또는 특정 타입에 대한 분기와 남은 경우를 모두 덮는 else 분기를 포함해요.

우리는 곧 비완전한 when 문을 금지할 계획이에요. when 표현식과 동작을 일관되게 만들기 위해서예요. 매끄러운 마이그레이션을 보장하려면, sealed 클래스나 Boolean에 대한 비완전한 when 문에 대한 경고를 보고하도록 컴파일러를 구성할 수 있어요. 그런 경고는 Kotlin 1.6에서 기본으로 나타나고 나중에는 오류가 될 거예요.

sealed class Mode {
    object ON : Mode()
    object OFF : Mode()
}

fun main() {
    val x: Mode = Mode.ON
    when (x) { 
        Mode.ON -> println("ON")
    }
// WARNING: Non exhaustive 'when' statements on sealed classes/interfaces 
// will be prohibited in 1.7, add an 'OFF' or 'else' branch instead

    val y: Boolean = true
    when (y) {  
        true -> println("true")
    }
// WARNING: Non exhaustive 'when' statements on Booleans will be prohibited 
// in 1.7, add a 'false' or 'else' branch instead
}

Kotlin 1.5.30에서 이 기능을 켜려면 언어 버전 1.6을 사용하면 돼요. 진행 모드(progressive mode)를 켜서 경고를 오류로 바꿀 수도 있어요:

kotlin {
    sourceSets.all {
        languageSettings.apply {
            languageVersion = "1.6"
            //progressiveMode = true // 기본은 false
        }
    }
}
kotlin {
    sourceSets.all {
        languageSettings {
            languageVersion = '1.6'
            //progressiveMode = true // 기본은 false
        }
    }
}

수퍼타입으로서의 suspend 함수

Kotlin 1.5.30은 몇 가지 제한과 함께 suspend 함수형 타입을 수퍼타입으로 사용하는 능력의 프리뷰를 제공해요.

class MyClass: suspend () -> Unit {
    override suspend fun invoke() { TODO() }
}

이 기능을 켜려면 -language-version 1.6 컴파일러 옵션을 사용하면 돼요:

kotlin {
    sourceSets.all {
        languageSettings.apply {
            languageVersion = "1.6"
        }
    }
}
kotlin {
    sourceSets.all {
        languageSettings {
            languageVersion = '1.6'
        }
    }
}

이 기능에는 다음과 같은 제한이 있어요:

  • 수퍼타입으로 일반 함수형 타입과 suspend 함수형 타입을 섞을 수 없어요. JVM 백엔드에서 suspend 함수형 타입의 구현 세부사항 때문이에요. JVM 백엔드에서는 그것들이 마커 인터페이스를 가진 일반 함수형 타입으로 표현돼요. 마커 인터페이스 때문에 어떤 슈퍼인터페이스가 suspend이고 어떤 것이 일반인지 구분할 방법이 없어요.
  • 여러 개의 suspend 함수형 수퍼타입을 사용할 수 없어요. 타입 검사가 있으면 여러 개의 일반 함수형 수퍼타입도 사용할 수 없어요.

실험적 API의 암시적 사용에 opt-in 요구

라이브러리 작성자는 실험적 API를 opt-in이 필요한 것으로 표시해서 사용자에게 그 실험 상태를 알릴 수 있어요. 컴파일러는 API가 사용될 때 경고 또는 오류를 올리고, 그것을 억제하려면 명시적 동의를 요구해요.

Kotlin 1.5.30에서 컴파일러는 시그니처에 실험적 타입이 있는 모든 선언을 실험적인 것으로 취급해요. 즉 실험적 API의 암시적 사용에도 opt-in을 요구해요. 예를 들어 함수의 반환 타입이 실험적 API 요소로 표시되어 있으면, 그 선언이 명시적으로 opt-in 요구로 표시되지 않았더라도 그 함수를 사용하는 데 opt-in이 필요해요.

// 라이브러리 코드

@RequiresOptIn(message = "This API is experimental.")
@Retention(AnnotationRetention.BINARY)
@Target(AnnotationTarget.CLASS)
annotation class MyDateTime // Opt-in 요구 애노테이션

@MyDateTime
class DateProvider // opt-in이 필요한 클래스

// 클라이언트 코드

// 경고: experimental API 사용
fun createDateSource(): DateProvider { /* ... */ }

fun getDate(): Date {
    val dateSource = createDateSource() // 역시 경고: experimental API 사용
    // ... 
}

opt-in 요구 사항에 대해 더 알아보세요.

다른 타깃에서 opt-in 요구 애노테이션 사용의 변화

Kotlin 1.5.30은 다른 타깃에서 opt-in 요구 애노테이션을 사용하고 선언하는 새 규칙을 제시해요. 이제 컴파일러는 컴파일 시점에 처리하기 어려운 사용 사례에 대해 오류를 보고해요. Kotlin 1.5.30에서:

  • 사용 지점에서 로컬 변수와 값 파라미터를 opt-in 요구 애노테이션으로 표시하는 것은 금지돼요.
  • 기본 선언도 표시된 경우에만 override를 표시할 수 있어요.
  • 백킹 필드와 getter를 표시하는 것은 금지돼요. 대신 기본 프로퍼티를 표시할 수 있어요.
  • opt-in 요구 애노테이션 선언 지점에서 TYPETYPE_PARAMETER 애노테이션 타깃을 설정하는 것은 금지돼요.

opt-in 요구 사항에 대해 더 알아보세요.

재귀적 제네릭 타입의 타입 추론 개선

Kotlin과 Java에서는 타입 파라미터에서 자기 자신을 참조하는 재귀적 제네릭 타입을 정의할 수 있어요. Kotlin 1.5.30에서 Kotlin 컴파일러는 타입 인자가 재귀적 제네릭이면 해당 타입 파라미터의 상한(upper bound)만으로도 타입 인자를 추론할 수 있어요. 이 덕분에 Java에서 빌더 API를 만들 때 자주 쓰는 재귀적 제네릭 타입의 다양한 패턴을 만들 수 있어요.

// Kotlin 1.5.20
val containerA = PostgreSQLContainer<Nothing>(DockerImageName.parse("postgres:13-alpine")).apply {
    withDatabaseName("db")
    withUsername("user")
    withPassword("password")
    withInitScript("sql/schema.sql")
}

// Kotlin 1.5.30
val containerB = PostgreSQLContainer(DockerImageName.parse("postgres:13-alpine"))
    .withDatabaseName("db")
    .withUsername("user")
    .withPassword("password")
    .withInitScript("sql/schema.sql")

-Xself-upper-bound-inference 또는 -language-version 1.6 컴파일러 옵션을 전달해서 개선을 켤 수 있어요. 새로 지원되는 사용 사례의 다른 예시는 이 YouTrack 티켓에서 볼 수 있어요.

빌더 추론 제한 제거

빌더 추론은 람다 인자 안의 다른 호출의 타입 정보를 바탕으로 호출의 타입 인자를 추론하게 해주는 특별한 종류의 타입 추론이에요. buildList()sequence() 같은 제네릭 빌더 함수를 호출할 때 유용할 수 있어요: buildList { add("string") }.

이전에는 그런 람다 인자 안에서 빌더 추론이 추론하려는 타입 정보를 사용하는 데 제한이 있었어요. 즉 그 타입을 지정할 수만 있고 얻을 수는 없었어요. 예를 들어 buildList()의 람다 인자 안에서 명시적 타입 인자 없이 get()을 호출할 수 없었어요.

Kotlin 1.5.30은 -Xunrestricted-builder-inference 컴파일러 옵션으로 이 제한들을 제거해요. 제네릭 빌더 함수의 람다 인자 안에서 이전에는 금지됐던 호출을 켜려면 이 옵션을 추가하면 돼요:

@kotlin.ExperimentalStdlibApi
val list = buildList {
    add("a")
    add("b")
    set(1, null)
    val x = get(1)
    if (x != null) {
        removeAt(1)
    }
}

@kotlin.ExperimentalStdlibApi
val map = buildMap {
    put("a", 1)
    put("b", 1.1)
    put("c", 2f)
}

또한 -language-version 1.6 컴파일러 옵션으로 이 기능을 켤 수 있어요.

Kotlin/JVM

Kotlin 1.5.30에서 Kotlin/JVM은 다음 기능을 받아요:

JVM 플랫폼에서 Kotlin Gradle 플러그인 업데이트는 Gradle 섹션을 참고하세요.

애노테이션 클래스 인스턴스화

Kotlin 1.5.30부터 임의의 코드에서 애노테이션 클래스의 생성자를 호출해서 결과 인스턴스를 얻을 수 있어요. 이 기능은 애노테이션 인터페이스를 구현하는 것을 허용하는 Java 관례와 같은 사용 사례를 다뤄요.

annotation class InfoMarker(val info: String)

fun processInfo(marker: InfoMarker) = ...

fun main(args: Array<String>) {
    if (args.size != 0)
        processInfo(getAnnotationReflective(args))
    else
        processInfo(InfoMarker("default"))
}

이 기능을 켜려면 -language-version 1.6 컴파일러 옵션을 사용하면 돼요. 비-val 파라미터나 2차 생성자와 다른 멤버를 정의하지 못하는 제한 같은 현재 애노테이션 클래스 제한들은 그대로 유지된다는 점을 유의하세요.

애노테이션 클래스 인스턴스화를 이 KEEP에서 더 알아보기.

개선된 nullability 애노테이션 지원 구성

Kotlin 컴파일러는 Java에서 nullability 정보를 얻기 위해 다양한 종류의 nullability 애노테이션을 읽을 수 있어요. 이 정보 덕분에 Kotlin에서 Java 코드를 호출할 때 nullability 불일치를 보고할 수 있어요.

Kotlin 1.5.30에서 특정 종류의 nullability 애노테이션의 정보를 바탕으로 컴파일러가 nullability 불일치를 보고할지 여부를 지정할 수 있어요. -Xnullability-annotations=@<패키지-이름>:<보고-수준> 컴파일러 옵션을 사용하면 돼요. 인자에서 정규화된 nullability 애노테이션 패키지와 다음 보고 수준 중 하나를 지정해요:

  • nullability 불일치를 무시하려면 ignore
  • 경고를 보고하려면 warn
  • 오류를 보고하려면 strict

정규화된 패키지 이름과 함께 지원되는 nullability 애노테이션의 전체 목록을 확인하세요.

새로 지원되는 RxJava 3 nullability 애노테이션에 대해 오류 보고를 켜는 예시는 다음과 같아요: [email protected]:strict. 그런 nullability 불일치는 모두 기본적으로 경고라는 점을 유의하세요.

Kotlin/Native

Kotlin/Native는 다양한 변화와 개선을 받았어요:

Apple silicon 지원

Kotlin 1.5.30은 Apple silicon에 대한 네이티브 지원을 도입해요.

이전에는 Kotlin/Native 컴파일러와 툴링이 Apple silicon 호스트에서 동작하려면 Rosetta 변환 환경이 필요했어요. Kotlin 1.5.30에서는 변환 환경이 더 이상 필요 없어요. 컴파일러와 툴링이 추가 작업 없이 Apple silicon 하드웨어에서 실행될 수 있어요.

또한 Kotlin 코드가 Apple silicon에서 네이티브로 실행되게 하는 새 타깃들도 도입했어요:

  • macosArm64
  • iosSimulatorArm64
  • watchosSimulatorArm64
  • tvosSimulatorArm64

이 타깃들은 Intel 기반과 Apple silicon 호스트 모두에서 사용 가능해요. 기존 타깃들도 Apple silicon 호스트에서 모두 사용 가능해요.

1.5.30에서는 kotlin-multiplatform Gradle 플러그인에서 Apple silicon 타깃에 대한 기본 지원만 제공한다는 점을 유의하세요. 특히 새 시뮬레이터 타깃은 ios, tvos, watchos 타깃 단축키에 포함되지 않아요. 우리는 새 타깃으로 사용자 경험을 개선하기 위해 계속 노력할 거예요.

CocoaPods Gradle 플러그인용 개선된 Kotlin DSL

Kotlin/Native 프레임워크용 새 파라미터

Kotlin 1.5.30은 Kotlin/Native 프레임워크용으로 개선된 CocoaPods Gradle 플러그인 DSL을 도입해요. 프레임워크 이름 외에도 Pod 구성에서 다른 파라미터를 지정할 수 있어요:

  • 프레임워크의 동적 또는 정적 버전 지정
  • 의존성 내보내기를 명시적으로 켜기
  • Bitcode 임베딩 켜기

새 DSL을 사용하려면 프로젝트를 Kotlin 1.5.30으로 업데이트하고 build.gradle(.kts) 파일의 cocoapods 섹션에서 파라미터를 지정하면 돼요:

cocoapods {
    frameworkName = "MyFramework" // 이 프로퍼티는 deprecated 이고
    // 향후 버전에서 제거될 예정
    // 프레임워크 설정을 위한 새 DSL:
    framework {
        // 모든 Framework 프로퍼티가 지원된다
        // 프레임워크 이름 설정. deprecated 'frameworkName' 대신
        // 이 프로퍼티를 사용한다
        baseName = "MyFramework"
        // Dynamic 프레임워크 지원
        isStatic = false
        // 의존성 내보내기
        export(project(":anotherKMMModule"))
        transitiveExport = false // 이것이 기본값
        // Bitcode 임베딩
        embedBitcode(BITCODE)
    }
}

Xcode 구성의 커스텀 이름 지원

Kotlin CocoaPods Gradle 플러그인은 Xcode 빌드 구성의 커스텀 이름을 지원해요. Xcode에서 Staging 같은 특별한 이름을 빌드 구성에 사용하고 있다면 도움이 돼요.

커스텀 이름을 지정하려면 build.gradle(.kts) 파일의 cocoapods 섹션에서 xcodeConfigurationToNativeBuildType 파라미터를 사용하면 돼요:

cocoapods {
    // 커스텀 Xcode 구성과 NativeBuildType 매핑
    xcodeConfigurationToNativeBuildType["CUSTOM_DEBUG"] = NativeBuildType.DEBUG
    xcodeConfigurationToNativeBuildType["CUSTOM_RELEASE"] = NativeBuildType.RELEASE
}

이 파라미터는 Podspec 파일에 나타나지 않아요. Xcode가 Gradle 빌드 과정을 실행하면 Kotlin CocoaPods Gradle 플러그인이 필요한 네이티브 빌드 타입을 선택해요.

Swift 5.5 async/await와의 실험적 상호 운용성

우리는 1.4.0에서 Objective-C와 Swift에서 Kotlin의 suspend 함수 호출을 지원했고, 이제 새 Swift 5.5 기능인 async와 await 한정자를 사용한 동시성을 따라잡기 위해 개선하고 있어요.

Kotlin/Native 컴파일러는 이제 nullable 반환 타입을 가진 suspend 함수에 대해 생성된 Objective-C 헤더에 _Nullable_result 속성을 방출해요. 이 덕분에 Swift에서 적절한 nullability를 가진 async 함수로 호출할 수 있어요.

이 기능은 실험적이고 향후 Kotlin과 Swift 양쪽의 변화에 영향을 받을 수 있다는 점을 유의하세요. 지금은 특정 제한이 있는 이 기능의 프리뷰를 제공하고 있으며, 여러분의 의견을 듣고 싶어요. 현재 상태에 대해 더 알아보고 피드백을 남기려면 이 YouTrack 이슈를 참고하세요.

객체와 동반 객체를 위한 개선된 Swift/Objective-C 매핑

이제 객체와 동반 객체를 네이티브 iOS 개발자에게 더 직관적인 방식으로 얻을 수 있어요. 예를 들어 Kotlin에 다음 객체들이 있다면:

object MyObject {
    val x = "Some value"
}

class MyClass {
    companion object {
        val x = "Some value"
    }
}

Swift에서 sharedcompanion 프로퍼티로 접근할 수 있어요:

MyObject.shared
MyObject.shared.x
MyClass.companion
MyClass.Companion.shared

Swift/Objective-C 상호 운용성 더 알아보기.

MinGW 타깃의 import 라이브러리 없는 DLL 링크 deprecation

LLD는 LLVM 프로젝트의 링커예요. 우리는 기본 ld.bfd보다 나은 점, 특히 더 나은 성능 때문에 MinGW 타깃의 Kotlin/Native에서 LLD를 사용할 계획이에요.

그러나 LLD의 최신 안정 버전은 MinGW(Windows) 타깃에서 DLL에 대한 직접 링크를 지원하지 않아요. 그런 링크는 import 라이브러리를 사용해야 해요. Kotlin/Native 1.5.30에서는 그것이 필요 없지만, 그런 사용이 향후 MinGW의 기본 링커가 될 LLD와 호환되지 않는다는 것을 알리기 위해 경고를 추가하고 있어요.

LLD 링커로의 전환에 대한 생각과 우려는 이 YouTrack 이슈에서 공유해 주세요.

Kotlin Multiplatform

1.5.30은 Kotlin Multiplatform에 다음 주목할 만한 업데이트를 가져와요:

공유 네이티브 코드에서 커스텀 cinterop 라이브러리 사용 능력

Kotlin Multiplatform은 공유 소스셋에서 플랫폼 의존적인 interop 라이브러리를 사용하는 옵션을 제공해요. 1.5.30 이전에는 이것이 Kotlin/Native 배포판과 함께 제공되는 플랫폼 라이브러리에서만 동작했어요. 1.5.30부터는 커스텀 cinterop 라이브러리에서도 사용할 수 있어요. 이 기능을 켜려면 gradle.propertieskotlin.mpp.enableCInteropCommonization=true 프로퍼티를 추가하면 돼요:

kotlin.mpp.enableGranularSourceSetsMetadata=true
kotlin.native.enableDependencyPropagation=false
kotlin.mpp.enableCInteropCommonization=true

XCFrameworks 지원

이제 모든 Kotlin Multiplatform 프로젝트가 출력 형식으로 XCFrameworks를 가질 수 있어요. Apple은 유니버설(fat) 프레임워크를 대체하기 위해 XCFrameworks를 도입했어요. XCFrameworks를 사용하면:

  • 모든 타깃 플랫폼과 아키텍처에 대한 로직을 단일 번들에 모을 수 있어요.
  • 애플리케이션을 App Store에 퍼블리싱하기 전에 불필요한 아키텍처를 제거할 필요가 없어요.

XCFrameworks는 Apple M1에서 기기와 시뮬레이터에 Kotlin 프레임워크를 사용하려 할 때 유용해요.

XCFrameworks를 사용하려면 build.gradle(.kts) 스크립트를 업데이트하면 돼요:

import org.jetbrains.kotlin.gradle.plugin.mpp.apple.XCFramework

plugins {
    kotlin("multiplatform")
}

kotlin {
    val xcf = XCFramework()
  
    ios {
        binaries.framework {
            baseName = "shared"
            xcf.add(this)
        }
    }
    watchos {
        binaries.framework {
            baseName = "shared"
            xcf.add(this)
        }
    }
    tvos {
        binaries.framework {
            baseName = "shared"
            xcf.add(this)
        }
    }
}
import org.jetbrains.kotlin.gradle.plugin.mpp.apple.XCFrameworkConfig

plugins {
    id 'org.jetbrains.kotlin.multiplatform'
}

kotlin {
    def xcf = new XCFrameworkConfig(project)

    ios {
        binaries.framework {
            baseName = "shared"
            xcf.add(it)
        }
    }
    watchos {
        binaries.framework {
            baseName = "shared"
            xcf.add(it)
        }
    }
    tvos {
        binaries.framework {
            baseName = "shared"
            xcf.add(it)
        }
    }
}

XCFrameworks를 선언하면 다음 새 Gradle 태스크들이 등록돼요:

  • assembleXCFramework
  • assembleDebugXCFramework (dSYMs를 포함하는 디버그 아티팩트를 추가로)
  • assembleReleaseXCFramework

XCFrameworks에 대해 이 WWDC 영상에서 더 알아보세요.

Android 아티팩트의 새 기본 퍼블리싱 설정

maven-publish Gradle 플러그인을 사용하면 빌드 스크립트에서 Android 변형(variant) 이름을 지정해서 Android 타깃용 멀티플랫폼 라이브러리를 퍼블리시할 수 있어요. Kotlin Gradle 플러그인이 퍼블리케이션을 자동으로 생성해요.

1.5.30 이전에는 생성된 퍼블리케이션 메타데이터가 퍼블리시된 모든 Android 변형에 대해 빌드 타입 속성을 포함해서, 라이브러리 소비자가 사용하는 것과 같은 빌드 타입에서만 호환됐어요. Kotlin 1.5.30은 새 기본 퍼블리싱 설정을 도입해요:

  • 프로젝트가 퍼블리시하는 모든 Android 변형이 같은 빌드 타입 속성을 가지면, 퍼블리시된 변형은 빌드 타입 속성을 가지지 않으며 어떤 빌드 타입과도 호환돼요.
  • 퍼블리시된 변형들이 서로 다른 빌드 타입 속성을 가지면, release 값을 가진 것들만 빌드 타입 속성 없이 퍼블리시돼요. 이렇게 하면 release 변형은 소비자 쪽 어떤 빌드 타입과도 호환되고, non-release 변형은 일치하는 소비자 빌드 타입에서만 호환돼요.

옵트아웃해서 모든 변형에 빌드 타입 속성을 유지하려면 이 Gradle 프로퍼티를 설정하면 돼요: kotlin.android.buildTypeAttribute.keep=true.

Kotlin/JS

Kotlin/JS에는 1.5.30으로 두 가지 주요 개선이 와요:

JS IR 컴파일러 백엔드가 Beta 도달

1.4.0에서 Alpha로 소개된 Kotlin/JS용 IR 기반 컴파일러 백엔드가 Beta에 도달했어요.

이전에 프로젝트를 새 백엔드로 마이그레이션하는 데 도움이 되는 JS IR 백엔드용 마이그레이션 가이드를 공개했어요. 이제 필요한 변화를 IntelliJ IDEA에서 바로 보여주는 Kotlin/JS Inspection Pack IDE 플러그인을 소개할게요.

Kotlin/JS IR 백엔드 애플리케이션을 위한 더 나은 디버깅 경험

Kotlin 1.5.30은 Kotlin/JS IR 백엔드용 JavaScript 소스 맵 생성을 가져와요. IR 백엔드가 켜져 있을 때 Kotlin/JS 디버깅 경험을 개선하며, 중단점, 스테핑, 적절한 소스 참조가 있는 읽기 쉬운 스택 트레이스를 포함한 완전한 디버깅 지원을 제공해요.

브라우저나 IntelliJ IDEA에서 Kotlin/JS를 디버깅하는 방법을 알아보세요.

Gradle

Kotlin Gradle 플러그인 사용자 경험 개선이라는 목표의 일환으로 다음 기능들을 구현했어요:

Java toolchains 지원

Gradle 6.7은 "Java toolchains support" 기능을 도입했어요. 이 기능을 사용하면:

  • Gradle과 다른 JDK 및 JRE로 컴파일, 테스트, 실행 파일을 실행할 수 있어요.
  • 미출시(unreleased) 언어 버전으로 코드를 컴파일하고 테스트할 수 있어요.

toolchains 지원 덕분에 Gradle은 로컬 JDK를 자동 감지하고 빌드에 필요한 없는 JDK를 설치할 수 있어요. 이제 Gradle 자신이 어떤 JDK에서든 실행될 수 있고 여전히 빌드 캐시 기능을 재사용할 수 있어요.

Kotlin Gradle 플러그인은 Kotlin/JVM 컴파일 태스크에 Java toolchains를 지원해요. Java toolchain은:

  • JVM 타깃에 사용 가능한 jdkHome 옵션을 설정해요.
  • 사용자가 jvmTarget 옵션을 명시적으로 설정하지 않았다면 kotlinOptions.jvmTarget을 toolchain의 JDK 버전으로 설정해요. toolchain이 구성되지 않으면 jvmTarget 필드는 기본값을 사용해요. JVM 타깃 호환성에 대해 더 알아보세요.
  • kapt 워커가 실행되는 JDK에 영향을 줘요.

toolchain을 설정하려면 다음 코드를 사용하면 돼요. <MAJOR_JDK_VERSION> 자리 표시자를 사용하려는 JDK 버전으로 바꿔 주세요:

kotlin {
    jvmToolchain {
        (this as JavaToolchainSpec).languageVersion.set(JavaLanguageVersion.of(<MAJOR_JDK_VERSION>)) // "8"
    }
}
kotlin {
    jvmToolchain {
        languageVersion.set(JavaLanguageVersion.of(<MAJOR_JDK_VERSION>)) // "8"
    }
}

kotlin 확장을 통해 toolchain을 설정하면 Java 컴파일 태스크에도 toolchain이 업데이트된다는 점을 유의하세요.

java 확장을 통해 toolchain을 설정할 수도 있고, Kotlin 컴파일 태스크가 그것을 사용해요:

java {
    toolchain {
        languageVersion.set(JavaLanguageVersion.of(<MAJOR_JDK_VERSION>)) // "8"
    }
}

KotlinCompile 태스크에 어떤 JDK 버전이든 설정하는 방법은 Task DSL로 JDK 버전 설정 문서를 살펴보세요.

Gradle 6.1부터 6.6 버전에서는 UsesKotlinJavaToolchain 인터페이스로 JDK 홈을 설정하세요.

UsesKotlinJavaToolchain 인터페이스로 JDK 홈 지정

kotlinOptions로 JDK 설정을 지원하는 모든 Kotlin 태스크는 이제 UsesKotlinJavaToolchain 인터페이스를 구현해요. JDK 홈을 설정하려면 JDK 경로를 넣고 <JDK_VERSION> 자리 표시자를 바꿔 주세요:

project.tasks
    .withType<UsesKotlinJavaToolchain>()
    .configureEach {
        it.kotlinJavaToolchain.jdk.use(
            "/path/to/local/jdk",
            JavaVersion.<LOCAL_JDK_VERSION>
        )
    }
project.tasks
    .withType(UsesKotlinJavaToolchain.class)
    .configureEach {
        it.kotlinJavaToolchain.jdk.use(
            '/path/to/local/jdk',
            JavaVersion.<LOCAL_JDK_VERSION>
        )
    }

Gradle 6.1부터 6.6 버전에서는 UsesKotlinJavaToolchain 인터페이스를 사용해요. Gradle 6.7부터는 Java toolchains를 대신 사용하세요.

이 기능을 사용할 때 kapt 태스크 워커프로세스 격리 모드만 사용하고, kapt.workers.isolation 프로퍼티는 무시된다는 점을 유의하세요.

Kotlin 데몬 JVM 인자를 명시적으로 지정하는 더 쉬운 방법

Kotlin 1.5.30에서는 Kotlin 데몬의 JVM 인자에 대한 새 로직이 있어요. 다음 목록의 각 옵션은 그 앞에 온 옵션들을 덮어써요:

  • 아무것도 지정하지 않으면 Kotlin 데몬은 Gradle 데몬에게서 인자를 상속받아요(이전처럼). 예를 들어 gradle.properties 파일에서:
    org.gradle.jvmargs=-Xmx1500m -Xms=500m
    
  • Gradle 데몬의 JVM 인자가 kotlin.daemon.jvm.options 시스템 프로퍼티를 가지면 이전처럼 사용해요:
    org.gradle.jvmargs=-Dkotlin.daemon.jvm.options=-Xmx1500m -Xms=500m
    
  • gradle.properties 파일에 kotlin.daemon.jvmargs 프로퍼티를 추가할 수 있어요:
    kotlin.daemon.jvmargs=-Xmx1500m -Xms=500m
    
  • kotlin 확장에서 인자를 지정할 수 있어요:
    kotlin {
        kotlinDaemonJvmArgs = listOf("-Xmx486m", "-Xms256m", "-XX:+UseParallelGC")
    }
    
    kotlin {
        kotlinDaemonJvmArgs = ["-Xmx486m", "-Xms256m", "-XX:+UseParallelGC"]
    }
    
  • 특정 태스크에 대해 인자를 지정할 수 있어요:
    tasks
        .matching { it.name == "compileKotlin" && it is CompileUsingKotlinDaemon }
        .configureEach {
            (this as CompileUsingKotlinDaemon).kotlinDaemonJvmArguments.set(listOf("-Xmx486m", "-Xms256m", "-XX:+UseParallelGC"))
        }
    
    tasks
        .matching {
            it.name == "compileKotlin" && it instanceof CompileUsingKotlinDaemon
        }
        .configureEach {
            kotlinDaemonJvmArguments.set(["-Xmx1g", "-Xms512m"])
        }
    

Kotlin 데몬에 대한 자세한 내용은 Kotlin 데몬과 Gradle에서 그것을 사용하는 방법을 참고하세요.

표준 라이브러리

Kotlin 1.5.30은 표준 라이브러리의 DurationRegex API에 개선을 가져와요:

Duration.toString() 출력 변경

Kotlin 1.5.30 이전에는 Duration.toString() 함수가 가장 간결하고 읽기 쉬운 숫자 값을 만드는 단위로 인자를 표현한 문자열을 반환했어요. 이제부터는 각각 자신의 단위인 숫자 컴포넌트들의 조합으로 표현된 문자열 값을 반환해요. 각 컴포넌트는 숫자 뒤에 단위의 축약 이름(d, h, m, s)이 오는 형태예요. 예를 들어:

이전 새로
12h 12h
1.5h 1h 30m
90m 1h 30m
0.5h 30m

음수 지속 시간이 표현되는 방식도 바뀌었어요. 음수 지속 시간은 마이너스 기호(-)가 앞에 붙고, 여러 컴포넌트로 구성되면 괄호로 둘러싸여요: -12m-(1h 30m)처럼요.

1초보다 작은 작은 지속 시간은 서브초 단위 중 하나와 함께 단일 숫자로 표현된다는 점을 유의하세요. 예를 들어 ms(밀리초), us(마이크로초), ns(나노초): 140.884ms, 500us, 24ns처럼요. 이들을 표현하는 데 과학적 표기법은 더 이상 사용되지 않아요.

지속 시간을 단일 단위로 표현하고 싶다면 오버로드된 Duration.toString(unit, decimals) 함수를 사용하면 돼요.

String에서 Duration 파싱

Kotlin 1.5.30에는 Duration API에 새 함수들이 있어요:

parse()parseOrNull() 사용 예시를 볼까요:

import kotlin.time.Duration
import kotlin.time.ExperimentalTime

@ExperimentalTime
fun main() {
//sampleStart
    val isoFormatString = "PT1H30M"
    val defaultFormatString = "1h 30m"
    val singleUnitFormatString = "1.5h"
    val invalidFormatString = "1 hour 30 minutes"
    println(Duration.parse(isoFormatString)) // "1h 30m"
    println(Duration.parse(defaultFormatString)) // "1h 30m"
    println(Duration.parse(singleUnitFormatString)) // "1h 30m"
    //println(Duration.parse(invalidFormatString)) // 예외 발생
    println(Duration.parseOrNull(invalidFormatString)) // "null"
//sampleEnd
}

parseIsoString()parseIsoStringOrNull() 사용 예시도 볼까요:

import kotlin.time.Duration
import kotlin.time.ExperimentalTime

@ExperimentalTime
fun main() {
//sampleStart
    val isoFormatString = "PT1H30M"
    val defaultFormatString = "1h 30m"
    println(Duration.parseIsoString(isoFormatString)) // "1h 30m"
    //println(Duration.parseIsoString(defaultFormatString)) // 예외 발생
    println(Duration.parseIsoStringOrNull(defaultFormatString)) // "null"
//sampleEnd
}

특정 위치에서 Regex와 매칭

Regex.matchAt()Regex.matchesAt() 함수는 String 또는 CharSequence의 특정 위치에서 정규식이 정확히 일치하는지 확인하는 방법을 제공해요.

matchesAt()는 불리언 결과를 반환해요:

fun main(){
//sampleStart
    val releaseText = "Kotlin 1.5.30 is released!"
    // 정규식: 숫자 하나, 점, 숫자 하나, 점, 숫자 하나 이상
    val versionRegex = "\\d[.]\\d[.]\\d+".toRegex()
    println(versionRegex.matchesAt(releaseText, 0)) // "false"
    println(versionRegex.matchesAt(releaseText, 7)) // "true"
//sampleEnd
}

matchAt()은 일치하는 것이 있으면 그 매치를, 없으면 null을 반환해요:

fun main(){
//sampleStart
    val releaseText = "Kotlin 1.5.30 is released!"
    val versionRegex = "\\d[.]\\d[.]\\d+".toRegex()
    println(versionRegex.matchAt(releaseText, 0)) // "null"
    println(versionRegex.matchAt(releaseText, 7)?.value) // "1.5.30"
//sampleEnd
}

Regex를 시퀀스로 분할

Regex.splitToSequence() 함수는 split()의 지연(lazy) 버전이에요. 주어진 정규식의 매치 주변에서 문자열을 나누지만, 결과를 Sequence로 반환해서 그 결과에 대한 모든 연산이 지연 실행돼요.

fun main(){
//sampleStart
    val colorsText = "green, red , brown&blue, orange, pink&green"
    val regex = "[,\\s]+".toRegex()
    val mixedColor = regex.splitToSequence(colorsText)
        .onEach { println(it) }
        .firstOrNull { it.contains('&') }
    println(mixedColor) // "brown&blue"
//sampleEnd
}

비슷한 함수가 CharSequence에도 추가됐어요:

    val mixedColor = colorsText.splitToSequence(regex)

Serialization 1.3.0-RC

kotlinx.serialization 1.3.0-RC는 새 JSON 직렬화 기능들과 함께 와요:

  • Java IO 스트림 직렬화
  • 기본값에 대한 프로퍼티 수준 제어
  • 직렬화에서 null 값 제외 옵션
  • 다형성 직렬화에서 커스텀 클래스 판별자(discriminator)

changelog에서 더 알아보세요.

더 알아보기