Kotlin 1.7.20의 새로운 기능

Kotlin 1.7.20의 새로운 기능

Kotlin 1.7.20이 공개됐어요! 이번 릴리스의 주요 하이라이트는 다음과 같아요:

이 영상에서 변경 사항에 대한 짧은 개요를 확인할 수도 있어요.

출처: What's new in Kotlin 1.7.20

본문

Kotlin K2 컴파일러 플러그인 지원

Kotlin 팀은 K2 컴파일러를 계속 안정화하고 있어요. K2는 여전히 Alpha 단계에 있지만(Kotlin 1.7.0 릴리스에서 발표), 이제 여러 컴파일러 플러그인을 지원해요. 새 컴파일러에 대한 Kotlin 팀의 업데이트를 확인하려면 이 YouTrack 이슈를 따라가 보세요.

이 1.7.20 릴리스부터 Kotlin K2 컴파일러는 다음 플러그인들을 지원해요:

새 컴파일러와 그 장점에 대해 더 알아보려면 다음 영상들을 확인하세요:

Kotlin K2 컴파일러를 활성화하는 방법

Kotlin K2 컴파일러를 활성화하고 테스트하려면 다음 컴파일러 옵션을 사용하세요:

-Xuse-k2

이 옵션은 build.gradle(.kts) 파일에서 지정할 수 있어요:

tasks.withType<KotlinCompile> {
    kotlinOptions.useK2 = true
}
compileKotlin {
    kotlinOptions.useK2 = true
}

JVM 프로젝트에서 성능 향상을 확인하고 예전 컴파일러의 결과와 비교해 보세요.

새 K2 컴파일러에 피드백 남기기

어떤 형태의 피드백이든 정말 감사해요:

언어

Kotlin 1.7.20은 새 언어 기능의 프리뷰 버전을 소개하고, 빌더 타입 추론에 제약을 두어요:

개방형 범위를 만드는 ..< 연산자의 프리뷰

이번 릴리스에서는 새 ..< 연산자를 소개해요. Kotlin은 값의 범위를 표현하는 .. 연산자를 갖고 있어요. 새 ..< 연산자는 until 함수처럼 동작하면서 개방형 범위를 정의하게 도와줘요.

우리 연구에 따르면, 이 새 연산자가 개방형 범위를 더 잘 표현하고 상한(upper bound)이 포함되지 않는다는 사실을 분명하게 만들어 줘요.

when 표현식에서 ..< 연산자를 사용하는 예시를 볼게요:

when (value) {
    in 0.0..<0.25 -> // First quarter
    in 0.25..<0.5 -> // Second quarter
    in 0.5..<0.75 -> // Third quarter
    in 0.75..1.0 ->  // Last quarter  <- Note closed range here
}
표준 라이브러리 API 변경 사항

공통 Kotlin 표준 라이브러리의 kotlin.ranges 패키지에는 다음과 같은 새 타입과 연산이 추가될 거예요:

OpenEndRange<T> 인터페이스

개방형 범위를 표현하는 새 인터페이스는 기존 ClosedRange<T> 인터페이스와 매우 유사해요:

interface OpenEndRange<T : Comparable<T>> {
    // Lower bound
    val start: T
    // Upper bound, not included in the range
    val endExclusive: T
    operator fun contains(value: T): Boolean = value >= start && value < endExclusive
    fun isEmpty(): Boolean = start >= endExclusive
}
기존 반복 가능 범위에서 OpenEndRange 구현

개발자들이 상한이 제외된 범위를 얻어야 할 때, 현재는 until 함수를 사용해 같은 값들을 가진 닫힌 반복 가능 범위를 효과적으로 만들어 냈어요. 이러한 범위들을 OpenEndRange<T>를 받는 새 API에서 받아들일 수 있도록, 기존 반복 가능 범위인 IntRange, LongRange, CharRange, UIntRange, ULongRange에 그 인터페이스를 구현하려고 해요. 그래서 이들은 ClosedRange<T>OpenEndRange<T> 인터페이스를 동시에 구현하게 돼요.

class IntRange : IntProgression(...), ClosedRange<Int>, OpenEndRange<Int> {
    override val start: Int
    override val endInclusive: Int
    override val endExclusive: Int
}
표준 타입을 위한 rangeUntil 연산자

rangeUntil 연산자는 현재 rangeTo 연산자가 정의하는 것과 같은 타입과 조합에 제공될 거예요. 프로토타입 목적으로는 확장 함수로 제공하지만, 일관성을 위해 개방형 범위 API를 안정화하기 전에 나중에 멤버로 만들 계획이에요.

..< 연산자를 활성화하는 방법

..< 연산자를 사용하거나 자신의 타입에 그 연산자 규칙을 구현하려면, -language-version 1.8 컴파일러 옵션을 활성화하세요.

표준 타입의 개방형 범위를 지원하기 위해 도입된 새 API 요소들은, 평소 실험적 stdlib API와 마찬가지로 opt-in이 필요해요: @OptIn(ExperimentalStdlibApi::class). 또는 -opt-in=kotlin.ExperimentalStdlibApi 컴파일러 옵션을 사용할 수도 있어요.

새 연산자에 대해 이 KEEP 문서에서 더 읽어보기

data object를 통한 싱글턴과 sealed 클래스 계층의 문자열 표현 개선

이번 릴리스에서는 사용할 수 있는 새 object 선언 타입을 소개해요: data object. Data object는 개념적으로 일반 object 선언과 동일하게 동작하지만, 기본 제공되는 깔끔한 toString 표현을 갖고 있어요.

package org.example
object MyObject
data object MyDataObject

fun main() {
    println(MyObject) // org.example.MyObject@1f32e575
    println(MyDataObject) // MyDataObject
}

이 덕분에 data object 선언은 sealed 클래스 계층에 안성맞춤이에요. 거기서 data class 선언과 함께 쓸 수 있거든요. 이 스니펫에서 EndOfFile을 일반 object 대신 data object로 선언하면, toString을 수동으로 오버라이드할 필요 없이 깔끔한 toString을 얻게 되고, 함께 쓰이는 data class 정의와 대칭을 이뤄요:

sealed class ReadResult {
    data class Number(val value: Int) : ReadResult()
    data class Text(val value: String) : ReadResult()
    data object EndOfFile : ReadResult()
}

fun main() {
    println(ReadResult.Number(1)) // Number(value=1)
    println(ReadResult.Text("Foo")) // Text(value=Foo)
    println(ReadResult.EndOfFile) // EndOfFile
}

data object를 활성화하는 방법

코드에서 data object 선언을 사용하려면 -language-version 1.9 컴파일러 옵션을 활성화하세요. Gradle 프로젝트에서는 build.gradle(.kts)에 다음을 추가하면 돼요:

tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
    // ...
    kotlinOptions.languageVersion = "1.9"
}
compileKotlin {
    // ...
    kotlinOptions.languageVersion = '1.9'
}

data object에 대해 더 읽고 구현에 대한 피드백을 해당 KEEP 문서에 남겨주세요.

새 빌더 타입 추론 제약

Kotlin 1.7.20은 빌더 타입 추론의 사용에 몇 가지 주요 제약을 두는데, 이것이 여러분의 코드에 영향을 줄 수 있어요. 이 제약은 빌더 람다 함수를 포함하는 코드에 적용되며, 람다 자체를 분석하지 않고는 파라미터를 추론할 수 없는 경우가 해당돼요. 파라미터가 인자로 사용되는 경우죠. 이제 컴파일러는 이런 코드에 항상 오류를 표시하고 타입을 명시적으로 지정하라고 요구해요.

이것은 호환성을 깨는 변경이지만, 우리 연구에 따르면 이런 경우는 매우 드물어서, 제약이 여러분의 코드에 영향을 주지 않을 거예요. 영향을 받는다면 다음 경우들을 고려해 보세요:

  • 멤버를 숨기는 확장과 함께 사용하는 빌더 추론

    여러분의 코드에 빌더 추론 동안 사용될 같은 이름의 확장 함수가 있다면, 컴파일러가 오류를 표시해요:

    class Data {
        fun doSmth() {} // 1
    }
    
    fun <T> T.doSmth() {} // 2
    
    fun test() {
        buildList {
            this.add(Data())
            this.get(0).doSmth() // Resolves to 2 and leads to error
        }
    }
    

    코드를 고치려면 타입을 명시적으로 지정해야 해요:

    class Data {
        fun doSmth() {} // 1
    }
    
    fun <T> T.doSmth() {} // 2
    
    fun test() {
        buildList<Data> { // Type argument!
            this.add(Data())
            this.get(0).doSmth() // Resolves to 1
        }
    }
    
  • 여러 람다가 있는 빌더 추론에서 타입 인자를 명시적으로 지정하지 않는 경우

    빌더 추론에 람다 블록이 둘 이상 있으면 타입에 영향을 줘요. 오류를 막기 위해 컴파일러가 타입 지정을 요구해요:

    fun <T: Any> buildList(
        first: MutableList<T>.() -> Unit,
        second: MutableList<T>.() -> Unit
    ): List<T> {
        val list = mutableListOf<T>()
        list.first()
        list.second()
        return list
    }
    
    fun main() {
        buildList(
            first = { // this: MutableList<String>
                add("")
            },
            second = { // this: MutableList<Int>
                val i: Int = get(0)
                println(i)
            }
        )
    }
    

    오류를 고치려면 타입을 명시적으로 지정하고 타입 불일치(type mismatch)를 수정해야 해요:

    fun main() {
        buildList<Int>(
            first = { // this: MutableList<Int>
                add(0)
            },
            second = { // this: MutableList<Int>
                val i: Int = get(0)
                println(i)
            }
        )
    }
    

위에서 언급한 경우에 해당하지 않는다면 이슈를 등록해 우리 팀에 알려주세요.

이 빌더 추론 업데이트에 대한 더 자세한 정보는 이 YouTrack 이슈를 참고하세요.

Kotlin/JVM

Kotlin 1.7.20은 제네릭 인라인 클래스를 소개하고, 위임 프로퍼티에 더 많은 바이트코드 최적화를 추가하며, kapt 스텁 생성 작업에서 IR을 지원해 kapt와 함께 모든 최신 Kotlin 기능을 사용할 수 있게 해줘요:

제네릭 인라인 클래스

Kotlin 1.7.20에서는 JVM 인라인 클래스의 기반 타입이 타입 파라미터가 될 수 있어요. 컴파일러는 이를 Any? 또는 일반적으로 타입 파라미터의 상한(upper bound)으로 매핑해요.

다음 예시를 볼게요:

@JvmInline
value class UserId<T>(val value: T)

fun compute(s: UserId<String>) {} // Compiler generates fun compute-<hashcode>(s: Any?)

이 함수는 인라인 클래스를 파라미터로 받아요. 파라미터는 타입 인자가 아니라 상한으로 매핑돼요.

이 기능을 활성화하려면 -language-version 1.8 컴파일러 옵션을 사용하세요.

이 기능에 대한 피드백을 YouTrack에 남겨주시면 감사하겠어요.

더 많은 위임 프로퍼티 최적화 사례

Kotlin 1.6.0에서 우리는 $delegate 필드를 생략하고 참조된 프로퍼티에 즉시 접근을 생성함으로써 프로퍼티에 위임하는 경우를 최적화했어요. 1.7.20에서는 이 최적화를 더 많은 경우에 구현했어요. 다음의 경우 위임자라면 이제 $delegate 필드가 생략돼요:

  • 이름 있는 객체:

    object NamedObject {
        operator fun getValue(thisRef: Any?, property: KProperty<*>): String = ...
    }
    
    val s: String by NamedObject
    
  • 같은 모듈에 backing field와 기본 getter가 있는 final val 프로퍼티:

    val impl: ReadOnlyProperty<Any?, String> = ...
    
    class A {
        val s: String by impl
    }
    
  • 상수 표현식, enum 항목, this, 또는 null. 여기 this의 예시가 있어요:

    class A {
        operator fun getValue(thisRef: Any?, property: KProperty<*>) ...
    
        val s by this
    }
    

위임 프로퍼티에 대해 더 알아보세요.

이 기능에 대한 피드백을 YouTrack에 남겨주시면 감사하겠어요.

kapt 스텁 생성 작업에서 JVM IR 백엔드 지원

1.7.20 이전에는 kapt 스텁 생성 작업이 예전 백엔드를 사용했고, 반복 가능한 애노테이션kapt에서 동작하지 않았어요. Kotlin 1.7.20부터 우리는 kapt 스텁 생성 작업에 JVM IR 백엔드 지원을 추가했어요. 이로써 반복 가능한 애노테이션을 포함한 모든 최신 Kotlin 기능을 kapt와 함께 사용할 수 있게 됐어요.

kapt에서 IR 백엔드를 사용하려면 gradle.properties 파일에 다음 옵션을 추가하세요:

kapt.use.jvm.ir=true

이 기능에 대한 피드백을 YouTrack에 남겨주시면 감사하겠어요.

Kotlin/Native

Kotlin 1.7.20은 새 Kotlin/Native 메모리 매니저가 기본값으로 활성화된 채 제공되며, Info.plist 파일을 커스터마이즈할 수 있는 옵션도 주어요:

새 Kotlin/Native 메모리 매니저가 기본값으로 활성화됨

이번 릴리스는 새 메모리 매니저에 더 많은 안정성과 성능 개선을 가져와서, 새 메모리 매니저를 Beta로 승격시킬 수 있게 됐어요.

이전 메모리 매니저는 동시성(concurrent)과 비동기 코드를 작성하기 어렵게 만들었어요. 여기에는 kotlinx.coroutines 라이브러리 구현의 문제도 포함돼요. 이 때문에 iOS와 Android 플랫폼 간 Kotlin 코드 공유에 문제가 생기는 동시성 제약이 있어서 Kotlin Multiplatform Mobile 도입이 막혔었어요. 새 메모리 매니저는 드디어 Kotlin Multiplatform Mobile을 Beta로 승격시키는 길을 열었어요.

새 메모리 매니저는 이전 릴리스와 비슷한 컴파일 시간을 만드는 컴파일러 캐시도 지원해요. 새 메모리 매니저의 장점에 대해 더 알아보려면 프리뷰 버전에 대한 원래 블로그 포스트를 참고하세요. 더 기술적인 세부 사항은 문서에서 볼 수 있어요.

구성과 설정

Kotlin 1.7.20부터 새 메모리 매니저가 기본값이에요. 추가 설정은 별로 필요하지 않아요.

이미 수동으로 켜 놓았다면 gradle.propertieskotlin.native.binary.memoryModel=experimental 옵션이나 build.gradle(.kts) 파일의 binaryOptions["memoryModel"] = "experimental"을 제거하면 돼요.

필요하다면 gradle.propertieskotlin.native.binary.memoryModel=strict 옵션으로 레거시 메모리 매니저로 되돌아갈 수 있어요. 하지만 레거시 메모리 매니저에는 더 이상 컴파일러 캐시 지원이 없으므로 컴파일 시간이 나빠질 수 있어요.

Freezing

새 메모리 매니저에서 freezing은 deprecated예요. 여러분의 코드가 레거시 매니저와 동작해야 한다면(거기서는 freezing이 여전히 필요) 사용하지 마세요. 이는 레거시 메모리 매니저 지원을 유지해야 하는 라이브러리 저자나, 새 메모리 매니저에서 문제를 만났을 때 대비책을 원하는 개발자에게 도움이 될 수 있어요.

이런 경우 새 메모리 매니저와 레거시 메모리 매니저 양쪽을 위한 코드를 임시로 지원할 수 있어요. deprecation 경고를 무시하려면 다음 중 하나를 하세요:

  • deprecated API 사용을 @OptIn(FreezingIsDeprecated::class)로 애노테이션합니다.
  • Gradle의 모든 Kotlin 소스셋에 languageSettings.optIn("kotlin.native.FreezingIsDeprecated")를 적용합니다.
  • -opt-in=kotlin.native.FreezingIsDeprecated 컴파일러 플래그를 전달합니다.

Swift/Objective-C에서 Kotlin suspend 함수 호출

새 메모리 매니저는 여전히 메인 스레드가 아닌 다른 스레드에서 Swift와 Objective-C의 Kotlin suspend 함수를 호출하는 것을 제한하지만, 새 Gradle 옵션으로 이를 해제할 수 있어요.

이 제한은 원래 레거시 메모리 매니저에서, 코드가 continuation을 원래 스레드에서 재개되도록 디스패치하는 경우 때문에 도입됐어요. 그 스레드에 지원되는 이벤트 루프가 없으면, 작업이 실행되지 않아 coroutine이 재개되지 않을 수 있었죠.

어떤 경우에는 이 제한이 더 이상 필요하지 않지만, 필요한 모든 조건을 확인하는 것은 쉽게 구현할 수 없어요. 그래서 우리는 새 메모리 매니저에서 이 제한을 유지하면서, 비활성화할 수 있는 옵션을 도입하기로 결정했어요. 이를 위해 gradle.properties에 다음 옵션을 추가하세요:

kotlin.native.binary.objcExportSuspendFunctionLaunchThreadRestriction=none

Kotlin 팀은 이 옵션을 구현해 준 Ahmed El-Helw에게 매우 감사해요.

피드백 남기기

이것은 우리 생태계에 중요한 변화예요. 더 나아지도록 여러분의 피드백을 감사히 받을게요.

프로젝트에서 새 메모리 매니저를 시도해 보고 이슈 트래커, YouTrack에서 피드백을 공유해 주세요.

Info.plist 파일 커스터마이징

프레임워크를 만들 때 Kotlin/Native 컴파일러는 정보 속성 목록 파일인 Info.plist를 생성해요. 이전에는 그 내용을 커스터마이즈하는 게 번거로웠어요. Kotlin 1.7.20부터는 다음 속성들을 직접 설정할 수 있어요:

Property Binary option
CFBundleIdentifier bundleId
CFBundleShortVersionString bundleShortVersionString
CFBundleVersion bundleVersion

이렇게 하려면 해당 binary option을 사용하세요. 필요한 프레임워크에 -Xbinary=$option=$value 컴파일러 플래그를 전달하거나 binaryOption(option, value) Gradle DSL을 설정하세요.

Kotlin 팀은 이 기능을 구현해 준 Mads Ager에게 매우 감사해요.

Kotlin/JS

Kotlin/JS는 개발자 경험을 개선하고 성능을 높이는 몇 가지 개선을 받았어요:

  • 의존성 로딩의 효율성 개선 덕분에, 증분 빌드와 클린 빌드 양쪽에서 klib 생성이 빨라졌어요.
  • 개발 바이너리의 증분 컴파일이 재작업되어 클린 빌드 시나리오에서 큰 개선, 더 빠른 증분 빌드, 그리고 안정성 수정을 가져왔어요.
  • 중첩 객체, sealed 클래스, 그리고 생성자의 기본값을 가진 파라미터에 대해 .d.ts 생성을 개선했어요.

Gradle

Kotlin Gradle 플러그인의 업데이트는 새 Gradle 기능과 최신 Gradle 버전과의 호환성에 초점을 맞추고 있어요.

Kotlin 1.7.20에는 Gradle 7.1 지원을 위한 변경 사항이 들어 있어요. Deprecated 메서드와 프로퍼티가 제거되거나 교체되어, Kotlin Gradle 플러그인이 만드는 deprecation 경고 수를 줄이고 향후 Gradle 8.0 지원을 가능하게 했어요.

다만 주의가 필요할 수 있는 호환성을 깨는 변경도 몇 가지 있어요:

타깃 구성

  • org.jetbrains.kotlin.gradle.dsl.SingleTargetExtension 이제 제네릭 파라미터인 SingleTargetExtension<T : KotlinTarget>을 가져요.
  • kotlin.targets.fromPreset() 컨벤션이 deprecated됐어요. 대신 여전히 kotlin.targets { fromPreset() }를 사용할 수 있지만, 타깃을 명시적으로 설정하는 것을 권장해요.
  • Gradle이 자동 생성하는 타깃 접근자(target accessor)는 더 이상 kotlin.targets { } 블록 안에서 사용할 수 없어요. 대신 findByName("targetName") 메서드를 사용하세요. 이런 접근자는 kotlin.targets의 경우에는 여전히 사용 가능하다는 점에 주의하세요. 예를 들어 kotlin.targets.linuxX64처럼요.

소스 디렉터리 구성

Kotlin Gradle 플러그인은 이제 Kotlin SourceDirectorySet을 Java의 SourceSet 그룹에 kotlin 확장으로 추가해요. 이로써 Java, Groovy, Scala에서 구성하는 것과 비슷하게 build.gradle.kts 파일에서 소스 디렉터리를 구성할 수 있게 됐어요:

sourceSets {
    main {
        kotlin {
            java.setSrcDirs(listOf("src/java"))
            kotlin.setSrcDirs(listOf("src/kotlin"))
        }
    }
}

이제 Kotlin의 소스 디렉터리를 지정하기 위해 deprecated Gradle 컨벤션을 사용할 필요가 없어요.

KotlinSourceSet에 접근하기 위해 kotlin 확장을 사용할 수도 있다는 점을 기억하세요:

kotlin {
    sourceSets {
        main {
        // ...
        }
    }
}

JVM 툴체인 구성의 새 메서드

이번 릴리스는 JVM 툴체인 기능을 활성화하는 새 jvmToolchain() 메서드를 제공해요. implementation이나 vendor 같은 추가 구성 필드가 필요 없다면, Kotlin 확장에서 이 메서드를 사용할 수 있어요:

kotlin {
    jvmToolchain(17)
}

이렇게 하면 추가 구성 없이 Kotlin 프로젝트 설정 과정이 단순해져요. 이번 릴리스 이전에는 JDK 버전을 다음 방식으로만 지정할 수 있었어요:

kotlin {
    jvmToolchain {
        languageVersion.set(JavaLanguageVersion.of(17))
    }
}

표준 라이브러리

Kotlin 1.7.20은 java.nio.file.Path 클래스에 대한 새 확장 함수를 제공하며, 이를 통해 파일 트리를 순회할 수 있어요:

  • walk()는 지정된 경로를 루트로 하는 파일 트리를 지연(lazily) 순회해요.
  • fileVisitor()FileVisitor를 별도로 만들 수 있게 해줘요. FileVisitor는 순회할 때 디렉터리와 파일에 대한 동작을 정의해요.
  • visitFileTree(fileVisitor: FileVisitor, ...)는 준비된 FileVisitor를 소비하고 내부에서 java.nio.file.Files.walkFileTree()를 사용해요.
  • visitFileTree(..., builderAction: FileVisitorBuilder.() -> Unit)builderAction으로 FileVisitor를 만들고 visitFileTree(fileVisitor, ...) 함수를 호출해요.
  • FileVisitor의 반환 타입인 FileVisitResult는 파일 처리를 계속하는 기본값 CONTINUE를 가져요.

다음은 이 새 확장 함수들로 할 수 있는 일들이에요:

  • FileVisitor를 명시적으로 만든 다음 사용하기:

    val cleanVisitor = fileVisitor {
        onPreVisitDirectory { directory, attributes ->
            // Some logic on visiting directories
            FileVisitResult.CONTINUE
        }
    
        onVisitFile { file, attributes ->
            // Some logic on visiting files
            FileVisitResult.CONTINUE
        }
    }
    
    // Some logic may go here
    
    projectDirectory.visitFileTree(cleanVisitor)
    
  • builderAction으로 FileVisitor를 만들고 즉시 사용하기:

    projectDirectory.visitFileTree {
    // Definition of the builderAction:
        onPreVisitDirectory { directory, attributes ->
            // Some logic on visiting directories
            FileVisitResult.CONTINUE
        }
    
        onVisitFile { file, attributes ->
            // Some logic on visiting files
            FileVisitResult.CONTINUE
        }
    }
    
  • walk() 함수로 지정된 경로를 루트로 하는 파일 트리 순회하기:

    @OptIn(kotlin.io.path.ExperimentalPathApi::class)
    fun traverseFileTree() {
        val cleanVisitor = fileVisitor {
            onPreVisitDirectory { directory, _ ->
                if (directory.name == "build") {
                    directory.toFile().deleteRecursively()
                    FileVisitResult.SKIP_SUBTREE
                } else {
                    FileVisitResult.CONTINUE
                }
            }
    
            onVisitFile { file, _ ->
                if (file.extension == "class") {
                    file.deleteExisting()
                }
                FileVisitResult.CONTINUE
            }
        }
    
        val rootDirectory = createTempDirectory("Project")
    
        rootDirectory.resolve("src").let { srcDirectory ->
            srcDirectory.createDirectory()
            srcDirectory.resolve("A.kt").createFile()
            srcDirectory.resolve("A.class").createFile()
        }
    
        rootDirectory.resolve("build").let { buildDirectory ->
            buildDirectory.createDirectory()
            buildDirectory.resolve("Project.jar").createFile()
        }
    
    // Use walk function:
        val directoryStructure = rootDirectory.walk(PathWalkOption.INCLUDE_DIRECTORIES)
            .map { it.relativeTo(rootDirectory).toString() }
            .toList().sorted()
        assertPrints(directoryStructure, "[, build, build/Project.jar, src, src/A.class, src/A.kt]")
    
        rootDirectory.visitFileTree(cleanVisitor)
    
        val directoryStructureAfterClean = rootDirectory.walk(PathWalkOption.INCLUDE_DIRECTORIES)
            .map { it.relativeTo(rootDirectory).toString() }
            .toList().sorted()
        assertPrints(directoryStructureAfterClean, "[, src, src/A.kt]")
    //sampleEnd
    }
    

실험적 API에서 평소와 같이, 새 확장에는 opt-in이 필요해요: @OptIn(kotlin.io.path.ExperimentalPathApi::class) 또는 @kotlin.io.path.ExperimentalPathApi. 또는 컴파일러 옵션인 -opt-in=kotlin.io.path.ExperimentalPathApi를 사용할 수 있어요.

walk() 함수visit 확장 함수에 대한 피드백을 YouTrack에 남겨주시면 감사하겠어요.

문서 업데이트

이전 릴리스 이후 Kotlin 문서에 몇 가지 주목할 만한 변경이 있었어요:

개편되고 개선된 페이지

  • 기본 타입 개요 – Kotlin에서 사용하는 기본 타입을 배워요: 숫자, Boolean, 문자, 문자열, 배열, 그리고 부호 없는 정수.
  • Kotlin 개발을 위한 IDE – 공식 Kotlin 지원이 있는 IDE와 커뮤니티 지원 플러그인이 있는 도구 목록을 확인하세요.

Kotlin Multiplatform 저널의 새 문서

새롭고 업데이트된 튜토리얼

릴리스 문서의 변경 사항

우리는 더 이상 각 릴리스에 대해 권장 kotlinx 라이브러리 목록을 제공하지 않아요. 이 목록에는 Kotlin 자체와 함께 권장되고 테스트된 버전만 담겨 있었거든요. 일부 라이브러리가 서로 의존하고 권장 Kotlin 버전과 다를 수 있는 특별한 kotlinx 버전을 요구한다는 사실을 고려하지 않았어요.

우리는 라이브러리들이 서로 어떻게 연관되고 의존하는지에 대한 정보를 제공하는 방법을 찾고 있어요. 그래서 프로젝트에서 Kotlin 버전을 업그레이드할 때 어떤 kotlinx 라이브러리 버전을 사용해야 하는지 명확해지도록 말이죠.

Kotlin 1.7.20 설치

IntelliJ IDEA 2021.3, 2022.1, 2022.2가 자동으로 Kotlin 플러그인을 1.7.20으로 업데이트하도록 제안해요.

새 명령줄 컴파일러는 GitHub 릴리스 페이지에서 다운로드할 수 있어요.

Kotlin 1.7.20 호환성 가이드

Kotlin 1.7.20은 증분 릴리스이지만, Kotlin 1.7.0에서 도입된 문제들의 확산을 막기 위해 어쩔 수 없이 호환성을 깨는 변경들도 있었어요.

이런 변경의 자세한 목록은 Kotlin 1.7.20 호환성 가이드에서 확인할 수 있어요.

더 알아보기