K2 compiler migration guideが公開された
K2 compilerになってどうなる
- K2 compilerになってfrontend compilerがKotlinで書き直された
- frontend compilerはsemantic analysis(セマンティック解析), call resolution(呼び出し解決), and type inference(型推論)を担当している
- compilerがコードを解析していざなにかをしようとするまでの時間が早くなるということ
- 以下ざーっと読んだ内容
- 解釈が間違っていれば指摘してください
Changes
Immediate initialization of open properties with backing fields
- openなpropertyの即時初期化が必要になる
- openなpropertyは基本的に初期値を与えるのが推奨されるが、どうしても初期化したくない場合は
final
を付与したpropertyする
open class Base {
open val a: Int
open var b: Int
init {
// Kotlin 1.9ではOK、Kotlin 2ではエラー
this.a = 1
// Kotln 1.9でもエラー
this.b = 1
}
}
class Derived : Base() {
override val a: Int = 2
override var b = 2
}
Deprecated synthetic setters on a projected receiver
- Genericsを取るJavaのsetter関数を異なる型で呼び出すとエラーになるようになる
public class Container<E> {
public E getFoo() {
return null;
}
public void setFoo(E foo) {}
}
fun exampleFunction(starProjected: Container<*>, inProjected: Container<in Number>, sampleString: String) {
// Kotlin 1.9以前もエラー
// Nothing型を要求するのでエラーになる
starProjected.setFoo(sampleString)
// `foo`は`setFoo()`に解決される, Nothing型を要求するが型不一致でもエラーにならず実行時にエラーとなる
// Kotlin 2からエラーになる
starProjected.foo = sampleString
// Kotlin 1.9以前もエラー
// 数値型を要求するのでエラーになる
inProjected.setFoo(sampleString)
// `foo`は`setFoo()`に解決される, 数値型を要求するが型不一致でもエラーにならず実行時にエラーとなる
// Kotlin 2からエラーになる
inProjected.foo = sampleString
}
Forbidden function calls with inaccessible types
- 解決できない型パラメータを持つラムダ関数をもつ関数の呼び出し、一部のGenericsがコンパイルエラーになるようになった
// module one
class Some(val x: Int)
// module two
// build.gradleでimplementation(module.one)している
// module twoはSome型にアクセスできるため両方の関数はコンパイルできる
fun foo(f: (Some, String) -> Unit) {}
fun bar(f: (Some) -> Unit) {}
class Generic<T>
fun gen() = Generic<Some<String>>()
fun takeString(g: Generic<Some<String>>) {}
// module three
// build.gradleでimplementation(module.two)している
fun test() {
// unused propertyがmodule threeではアクセスできないSome型のためKotlin 2ではエラーになる
foo { _, _ -> }
// module threeではアクセスできないSome型をとるためwarningになる
foo { some, str -> }
// Genericsで隠蔽されているためKotlin 2ではwarningになる
val z = gen()
// module threeでアクセスできないSome型の引数をとるためKotlin 2ではエラーになる
takeString(z)
}
Consistent resolution order of Kotlin properties and Java fields with the same name
- Javaクラスを継承したKotlinクラスで同名のプロパティを持つ際の解決が一貫性を持つようになった
- 基本的にサブクラスのpropertyが優先される
- Kotlinクラスを継承したJavaクラスも同様にサブクラスのプロパティが優先される
public class Base {
public String a = "a";
public String b = "b";
}
class Derived : Base() {
val a = "aa"
val b
get() = "bb"
}
fun main() {
val derived = Derived()
// Derived.aに解決される
println(derived.a)
// カスタムゲッターの挙動が変わる
// Kotlin 1.9以前 Base.bに解決される -> b
// Kotlin 2 Derived.bに解決される -> bb
println(derived.b)
}
Improved null safety for Java primitive arrays
- Kotlin 2からTYPE_USEなannotationが付与されているJava配列(Array)のnullabilityが正しく推論されるようになった
- TYPE_USEなannotation以外はKotlin 2でも変更はない
interface DataProvider {
int @Nullable [] fetchData();
}
val dataService: DataProvider = object : DataProvider {}
val data = dataService.fetchData()
// Kotlin 1.9以前
// dataはIntArrayに解決される
// 呼び出し時にNPEが発生する可能性がある
data[0]
// Kotlin 2
// dataはIntArray?に解決される
data?.get(0)
Smart cast improvements
Local variables and further scopes
-
if
, when
外で判定した条件がblock内で判定されるようになる
- instanceの型がなんなのかというのを意味にある名前で変数とできる
class Cat {
fun purr() {
println("Purr purr")
}
}
fun petAnimal(animal: Any) {
val isCat = animal is Cat
if (isCat) {
// Kotlin 1.9以前: すでに判定済みでもasやcheckで型を指定しないと呼べない
(animas as Cat).purr()
// Kotlin 2.0: if外の条件についてもfrontend compilerが考慮してくれる
animal.purr()
}
}
Type checks with the logical or operator
- or(||)の条件について正確に読み取ってくれるようになる
interface Status {
fun signal() {}
}
interface Ok : Status
interface Postponed : Status
interface Declined : Status
fun signalCheck(signalStatus: Any) {
if (signalStatus is Postponed || signalStatus is Declined) {
// Kotlin 1.9以前: asやcheckで型を指定しないと呼べない、かつasやcheckを行ったあとの行はStatusとしか認識できない
(signalStatus as Status).signal()
// Kotlin 2.0: Status interfaceであることは明示的なため呼べる
signalStatus.signal()
}
}
Inline functions
- inline関数内の変数観測が正しくなり余計なnullabilityを気にする必要がなくなった
interface Processor {
fun process()
}
inline fun inlineAction(f: () -> Unit) = f()
fun nextProcessor(): Processor? = null
fun runProcessor(): Processor? {
var processor: Processor? = null
inlineAction {
if (processor != null) {
// Kotlin 1.9以前:
processor?.process()
// Kotlin 2.0: 展開された関数中ではすでにnullチェック済みなのでそのまま呼べる
processor.process()
}
processor = nextProcessor()
}
return processor
}
Properties with function types
interface Provider {
operator fun invoke()
}
interface Processor : () -> String
class Holder(val provider: Provider?, val processor: Processor?) {
fun process() {
if (provider != null) {
// Kotlin 1.9以前: invokeを呼ぶ必要がある
provider.invoke()
// Kotlin 2.0: nullチェック済みなので直接operator functionを呼べる
provider()
}
if (processor != null) {
// Kotlin 1.9以前: invokeを呼ぶ必要がある
processor.invoke()
// Kotlin 2.0: nullチェック済みなので直接operator functionを呼べる
processor()
}
}
}
Exception handling
fun testString() {
var stringInput: String? = null
stringInput = ""
try {
// 文字列代入後なのでcompilerはnon-null判定
println(stringInput.length)
stringInput = null
// 処理途中でexceptionを投げる
if (2 > 1) throw Exception()
stringInput = ""
} catch (exception: Exception) {
// Kotlin 1.9以前: try句の前に文字列を代入しているのでStringでcatch句を書く(NullPointExceptionの可能性がある)
// Kotlin 2.0: stringInputがnullの可能性があるためString?でcatch句を書く
println(stringInput?.length)
}
}
Increment and decrement operators
interface Rho {
operator fun inc(): Sigma = TODO()
}
interface Sigma : Rho {
fun sigma() = Unit
}
interface Tau {
fun tau() = Unit
}
fun main(input: Rho) {
var unknownObject: Rho = input
if (unknownObject is Tau) {
// incrementでSigma型になる
++unknownObject
// Kotlin 1.9以前: unresolved reference
// Kotlin 2.0.0: callable
unknownObject.sigma()
// Kotlin 1.9以前: callableだが呼び出すとエラーになる
// Kotlin 2.0.0: 既にTau型でなくなっているのでunresolved reference
unknownObject.tau()
}
}
Kotlin Multiplatform improvements
Separation of common and platform sources during compilation
- KMPは共通で使用するcommonコードと各platformで使用するplatformコードをそれぞれ別に記述することができるため同名・同引数の関数を作成できる

- Kotlin 1.9以前ではcompile時にcommonコードとplatformコードをごっちゃにして取り扱っていた
- Kotlin 2.0ではcommonコードとplatformコードがisolateされる
- 同名関数をcommonコードとplatformコードを定義し呼び出した場合の挙動が変更される
- ただし引数の型が異なる関数を定義するなどoverloadを使用する場合は以前の挙動になったりするところもある
表. 同名関数をcommonコードとplatformコードを定義し呼び出した場合の挙動
--- |
Kotlin 1.9以前 |
Kotlin 2.0 |
JVM |
JVM platformコード |
commonコード |
JVM以外 |
commonコード |
commonコード |
Different visibility levels of expected and actual declarations
-
expect/actualはplatformごとの実装を分けるための仕組み
- Kotlin 1.9以前ではexpect/actualでそれぞれ定義したものは同一のvisibilityを設定する必要があった
- Kotlin 2.0ではstrictでない方向性(見える範囲が広がる方向)への変更は許容されるようになる
expect internal class Attribute
// internal -> publicなのでOK
actual class Attribute
// actualでtypealiasを使うときはvisibility設定はstrictでないようにならなければならない
expect internal class Attribute
internal actual typealias Attribute = Expanded
class Expanded
Compiler plugins support
- 公式のpluginは大体対応されている
- kaptも対応するらしい
- compose, KSPももちろん対応進んでいる
Tasks error in Gradle configuration cache
-
Gradle configuration cacheはプロジェクトの設定値を使い回すことで2回目以降のタスク実行を爆速にする仕組み
- 2.0.0-Beta4から
invocation of Task.project at execution time is unsupported
というconfiguration cacheのエラーが出ていた
- configuration cacheに対応していないタスクがあることで発生している
- Gradleチームが解決に取り組み中
Support in IntelliJ IDEA
- IntelliJ IDEA 2024.1から使える
- Android StudioはKoalaから使える
- Settings | Languages & Frameworks | Kotlin > Enable K2 Kotlin Modeのチェックボックスをいれる
Kotlin/JVM
Generate lambda functions using invokedynamic
-
invokedynamic
がラムダ関数のインスタンス化のデフォルトになった
-
invokedynamic
はKotlin 1.5でcompiler flag(-Xlambdas=indy
)を設定すると使えた
- 従来のラムダ関数の匿名クラスのインスタンス化よりバイナリサイズが減少する
-
invokedynamic
は現状いくつかの制限がある
- 生成された匿名クラスのインスタンスはシリアライズできない
-
reflect()
メソッドが対応していない
-
toString()
が読みづらい
-
@JvmSerializableLambda
をつけると個別に以前と同じ挙動にできる
-
-Xlambdas=class
を設定すると全体を以前と同じ挙動にできる
The kotlinx-metadata-jvm library is now Stable
- kotlinx-metadata-jvmが安定版になった
- kotlinx-metadata-jvmは
@Metadata
を読み取ることで、kotlin-reflectなしで黒魔術を使いやすくするライブラリ
- 安定版になったことで
kotlin-metadata-jvm
(x抜き)でartifactを取得できるようになった
Kotlin/Native
Resolving conflicts with Objective-C methods
- Obj-Cメソッドの衝突が解消された
- Obj-Cは同引数異名のメソッドを定義できるがKotlin上ではoverloadエラーが発生していた
- Kotlin 2.0で
@ObjCSignatureOverride
アノテーションが導入された
-
@ObjCSignatureOverride
を付与すると衝突するoverloadを無視するようになる
Changed log level for compiler arguments
- K/Nの
compile
, link
, cinterp
タスクのデフォルトログレベルがINFOからDEBUGに変更された
Kotlin/Wasm
Unsigned primitive types in functions with @JsExport
-
@JsExport
を使ってexportした関数で符号無しのPrimitiveな型が使えるようになった
Binaryen available by default in production builds
-
BinaryenはWASMの最適化ライブラリ
- プロダクションビルドで設定無しで使えるようになった
- 開発ビルドでは適用されない
Generation of TypeScript declaration files in Kotlin/Wasm
-
@JsExport
を付与した関数のTypeScript定義(.d.tsファイル)を生成できるようになった
-
kotlin {
wasmJs {
binaries.executable()
browser {}
generateTypeScriptDefinitions() // これを追加する
}
}
Support for named export
-
@JsExport
を付与した関数が名前付きでexportされるようになった
@JsExport
fun add(a: Int, b: Int) = a + b
// Kotlin 1.9以前
import exports from "./index.mjs"
exports.add(10, 10);
// Kotlin 2.0
import { add } from "./index.mjs"
add(10, 10);
withWasm() function is split into JS and WASI variants
- gradleのSourceSet設定時に使用する
withWasm()
関数がDeprecatedになった
kotlin {
applyHierarchyTemplate {
// Deprecated
withWasm()
// Kotlin 2.0
withWasmJs()
withWasmWasi()
}
}
New functionality to catch JavaScript exceptions
-
try-catch
を使ってJavaScript側のExceptionをcatchできるようになった
-
Throwable
もしくはJsException
としてcatchすることができる
- catchはできるがcall stackなどの情報はまだ取得できない
The new exception handling proposal is now supported
- Kotlin/WasmのWebAssembly例外処理のproposal実装が試せるようになった
- compiler flagに
-Xwasm-use-new-exception-proposal
を設定すると使える
-
-Xwasm-use-traps-instead-of-exceptions
など既存の機能とも互換性があるので一緒に使える
Kotlin/JS
New compilation target
- Kotlin/JSのcompile target設定方法が新しくなった
- この方法で設定するとES2015 classesとES2015 generatorsが有効になる
// Kotlin 1.9
kotlin {
js(IR) {
useEsModules() // Enables ES2015 modules
}
}
tasks.withType<KotlinJsCompile>().configureEach {
kotlinOptions {
useEsClasses = true
}
}
// Kotlin 2.0
kotlin {
js {
compilerOptions {
target.set("es2015")
}
}
}
Suspend functions as ES2015 generators
Passing arguments to the main function
kotlin {
js {
binary.executable()
// Deno.argsに引数が格納される
passAsArgumentToMainFunction("Deno.args")
// NodeJSの場合は`process.argv`に格納される
nodejs {
passProcessArgvToMainFunction()
}
}
}
Per-file compilation for Kotlin/JS projects
- ktファイルごとにjsファイルを生成するオプションが追加された
- Kotlin 1.9以前は1つのjsファイルにバンドルするかモジュール単位でjsファイルを生成するオプションがあった
- モジュール単位でも大きいjsファイルになる可能性があるためKotlin 2以降はktファイルごとjsファイルを生成することができるようになった
- ktファイルにexport宣言が含まれている場合はktファイルごとに2つのjsファイルが生成される
- compiler optionに
-Xir-per-file
を設定するかgradle.propertiesにkotlin.js.ir.output.granularity=per-file
を設定すると使える
Improved collection interoperability
- KotlinのSet, Map, ListなどのCollectionをexportできるようになった
- JavaScriptからKotlinのCollectionを作成することはまだできない
@JsExport
data class User(
val name: String,
val friends: List<User> = emptyList()
)
@JsExport
val me = User(
name = "Me",
firends = listOf(User(name = "Kodee"))
)
import { User, me, KtList } from "my-module"
const allMyFriendNames = me.friends
.asJsReadonlyArrayView()
.map(x => x.name)
Support for createInstance()
Support for type-safe plain JavaScript objects
- 型安全なJavaScriptオブジェクトがサポートされた
-
js()
関数でJavaScriptオブジェクトを使用すると実行時エラーもしくは一歳動かないが@JsPlainObject
を付与したinterfaceを使用することでコンパイル時にエラーを検知できるようになる
-
js-plain-objects
pluginを使用すると使える
-
js-plain-objects
pluginはK2のみをサポートしている
-
js-plain-objects
はinvoke関数とcopy関数を追加する
Support for npm package manager
- Kotlin 1.9まではYarnのみが使用できた
- gradle.propertiesを設定するとNPMを使用することができる
Gradle improvements
New Gradle DSL for compiler options in multiplatform projects
- Multiplatformプロジェクトでcompiler optionを一括設定できるようになった
- Kotlin 1.9以前ではTask, Compiler, SourceSetごとにしか設定できなかった
- Projectレベル、Targetレベル、Taskレベルの順に適用される(そのためProjectレベルで設定したオプションをTargetレベル、Taskレベルで上書きできる)
kotlin {
// Kotlin 2から使用できるようになった
compilerOptions {
allWarningsAsErrors.set(true)
}
jvm {
compilerOptions {
// `allWarningsAsErrors`と`noJdk`がJVMソースセットに適用される
noJdk.set(true)
}
}
tasks.named<KotlinJvmCompile>("compileKotlinJvm") {
compilerOptions {
// `allWarningsAsErrors`,`noJdk`,`verbose`がcompilerに適用される
verbose = true
}
}
}
New Compose compiler Gradle plugin
before |
after |
androidx.compose.compiler:compiler |
org.jetbrains.kotlin:kotlin-compose-compiler-plugin-embeddable |
androidx.compose.compiler:compiler-hosted |
org.jetbrains.kotlin:kotlin-compose-compiler-plugin |
// Kotlin 1.9
plugin {
id("com.android.application")
id("org.jetbrains.kotlin.android")
}
// android{}内でcomposeの設定を行う
android {
buildFeatures {
compose = true
}
composeOptions {
kotlinCompilerExtensionVersion = "1.5.13"
}
}
// Kotlin 2以降
plugin {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id("org.jetbrains.kotlin.plugin.compose")
}
// topレベルのcomposeCompiler{}内でcomposeの設定を行う
composeCompiler {
enableStrongSkippingMode = true
}
New attribute to distinguish JVM and Android published libraries
- JVMとandroid向けのライブラリを区別するattributeが全てのvariantで公開されるようになった
-
android
, standard-jvm
, no-jvm
の3種類がある
- JavaプロジェクトでJVM対応されているKMPライブラリを安全に使用できるようになった
- gradle.propertiesに
kotlin.publishJvmEnvironmentAttribute=false
を設定すると無効化できる
Improved Gradle dependency handling for CInteropProcess in Kotlin/Native
- Kotlin/Nativeでcinteropを使用する際のdefFile生成タスク周りが整理された
- Kotlin 1.9以前ではcinteropタスクで使用するdefFileが別のタスクの成果物であった場合、Gradleタスクの依存を設定して解決する必要があった
- Kotlin 2ではGradleの新しいプロパティである
RegularFileProperty
を使用してdefFileを参照するようになった
-
RegularFileProperty
を使用するとGradleはプロパティを遅延評価するようになるため別タスクの実行を待った上でcinteropタスクが実行されるようになる
kotlin {
macosArm64("native") {
compilations.getByName("main") {
// Kotlin 1.9
cinterop.create("cinterop-library") {
defFileProperty.set(createDefFileTask.flatMap { it.defFile.asFile })
project.tasks.named(interopProcessingTaskName).configure {
dependsOn(createDefFileTask)
}
}
// Kotlin 2
cinterop.create("cinterop-library") {
definitionFile.set(project.file("def-file.def"))
}
}
}
}
Visibility changes in Gradle
- Kotlin DSLを使用する際に適切にDSLを使用していない場合Warningログが表示されるようになった
kotlin {
// ここで`KotlinTargetContainerWithPresetFunctions.jvm()`を呼ぶべき
jvm()
sourceSets {
// `NamedDomainObjectContainer<KotlinSourceSet>.jvm()`は存在しないが`KotlinTargetContainerWithPresetFunctions.jvm()`を呼べてしまう
// Kotlin 2以降はWarningが表示される
jvm()
}
}
New directory for Kotlin data in Gradle projects
- Kotlin Gradle Pluginが
<project-root>/.kotlin
配下にcacheを保存するようになった
- gradle.propertiesを設定することで保存先と保存の有無を設定できる
kotlin.project.persistent.dir=<project-root-directory>/.kotlin # 保存先を変更できる
kotlin.project.persistent.dir.gradle.disableWrite=true # 保存を無効にする
Kotlin/Native compiler downloaded when needed
- Kotlin 1.9以前ではProjectにKotlin/Nativeターゲットが追加されているとconfigurationフェーズでKotlin Native Compilerがダウンロードされていた
- Kotlin/Navitveターゲットが設定されているがKotlin/Nativeコンパイルを行わない場合もダウンロードされていた
- ex)iOSターゲットが設定されているプロジェクトをLinuxマシンで開いた場合にもLinux向けのKotlin Native Compilerがダウンロードされてしまっていた
- CI上などでネットワーク、ディスクリソースを浪費していた
- Kotlin 2では実際にKotlin/Nativeコンパイルを実行するタイミングでダウンロードされるようになる
-
gradle.properties
にkotlin.native.toolchain.enabled=false
を設定すると以前の挙動に戻せる
Deprecating old ways of defining compiler options
-
HasCompilerOptions
のcompilerOptions
がdeprecatedになった
-
HasCompilerOptions
interfaceを用いて各DSLにcompipler optionを設定する方法が提供されていたがKotlinコンパイルタスクと同一のcompilerOptions
を提供するため混乱を招いていた
-
New Gradle DSL for compiler options in multiplatform projects
もこれの一環
- 各設定方法がドキュメントにあるのでそちらを読んで改めて設定するとよい
Bumped minimum AGP supported version
- Kotlin 2.0.0以降はAGP7.1.3以上である必要がある
New Gradle property to try latest language version
- 新しいKotlinバージョンを試すためのオプションが追加された
- gradle.propertiesに
kotlin.experimental.tryNext=true
を設定すると新しいKotlinバージョンを試せる
- Kotlin2.0のプロジェクトでオプションを設定するとKotlin2.1が使用できる
New JSON output format for build reports
- Kotlin 1.7でビルドのレポートをファイル形式で保存することができるようになった
- txtのみサポートされたいたがjson形式が新たに加わった
Standard library
Stable replacement of the enum class values generic function
-
enumEntries<T>()
がstableになった
-
enumEntries<T>()
はenumValues<T>()
を代替する関数
- Kotlin 1.8.20で導入された
Enum.entries
とEnum.values()
の関係と同じく, enumEntries<T>()
のほうがパフォーマンスが良いため置き換えることが推奨される
Stable AutoCloseable interface
-
AutoCloseable
はjava.lang.AutoCloseable
のKotlin版
-
try-with-resource
をKotlinで実現するinterface
- 実装しているクラスは
use
関数などを用いて適切にresource解放する実装ができる
Common protected property AbstractMutableList.modCount
Common protected function AbstractMutableList.removeRange
Common String.toCharArray(destination)