Kotlinアドベントカレンダー2020 ( https://qiita.com/advent-calendar/2020/kotlin)の代理投稿です。
jvmtargetとは
Kotlinが吐くJavaバイトコードがどのバージョンと互換があるかを示すものです。
Kotlinは元々IntelliJ IDEA用に作られた
以前にも触れましたが、KotlinはJetBrains社が欲しかったalt JavaなのでIntelliJ IDEAの実装とかで使われていました(ちなみに作った当初からマルチプラットフォームの構想は語られている)。IntelliJ のpluginとかもそうなのだけど、Java SE 6が対象となっています。ついでで、Androidにものっけやすかったため、Android開発者に目をつけられ人気が出たのは後の話。
why-jetbrains-needs-kotlinhttps://github.com/JetBrains/intellij-community/search?l=kotlin
jvmtargetのデフォルト
そんな経緯もあってか、何も指定せずにコンパイルするとjvmtargetは6のものが出力されます。
jvmtargetの選択肢
jvmtargetは2020/12現在以下を選択できます。
“1.6”, “1.8”, “9”, “10”, “11”, “12”, “13”, “14”, “15”
Javaのアップデートに詳しい人ならこの選択肢の意味が分かるはず。JDKバージョンですね。
しかし、昔の名残で1.xで刻んでたのか分からないですが、急に9から数え方変わってますね。
https://kotlinlang.org/docs/reference/using-gradle.html
以前は1.6と1.8だけでした。
9移行をサポートするようになったのは割と最近です。Kotlin1.3.30からなのでオッサンの僕には最近なのです(Kotlin1.4が最新なんですけどね)
jvmtargetを上げることで期待できるのはコンパイルの最適化です。新しい文法が使えるとかいうのはありません。(もちろんJDKの標準ライブラリは増えます)
強いていうなら、なんらかの都合でコンパイル後にJavaのinterfaceのデフォルトメソッドのシグネチャが必要になるなら8以上を選びましょう。最新のJavaを攻めたいなら15にするのもありかもしれません。安定択を取りたいたら大体のJDKでLTSである11を選ぶのが良いでしょう。ここは本番環境のバージョンと合わせとくので良いと思います。JDKのバージョンアップ計画に合わせていくのが良いです。
https://stackoverflow.com/questions/52888341/does-kotlin-support-java-11
Jvmアノテーション
正式名称じゃないですが、KotlinではJavaとのinteroperability(相互運用性)を保つためにJvmxxxというアノテーションをたくさん持っています。例えば、KotlinにはstaticメソッドはありませんがなんらかのJavaフレームワークの制約上staticメソッドのシグネチャを持つクラスが必要になる場合があります。こういう場合はJvmStaticでメソッドをマークすることでバイトコードレベルではstaticメソッドのシグネチャにする、といった手法です。
このあたりのわかりやすい説明はこれかもしれません。
https://developer.android.com/kotlin/interop?hl=ja
相互運用性のためのアノテーションってどのぐらいあるの?
2020/12現在、いろいろあります。
- JvmStatic
- JvmField
- JvmName
- JvmOverloads
- JvmDefault
- JvmDefaultWithoutCompatibility
- JvmMultiFileClass
- JvmSuppressWildcards
- JvmSynthetic
- JvmWildcard
- PurelyImplements
- Strictfp
- Synchronized
- Throws
- Transient
- Volatile
途中からJvmついてないやつがありますね。
strictfp、volatile、transientあたりはJavaやり込んでる人でも業務コードでほぼ出くわさないのではないでしょうか?っていうアレです。イマドキはsynchronizedとかもそうですかね。かなりマイナーな奴らです。PurelyImplementsもJavaとKotlinのプラットフォーム境界であえてKotlin実装を指定したい場合ぐらいしか使いません。
詳細はリファレンスにまとまってます
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.jvm/
Javaのinterfaceのデフォルトメソッド対応状況
JvmDefaultは1.2で追加されました。JvmDefaultアノテーションをマークし-Xjvm-defaultオプションをつけてコンパイルすることで、Kotlinのデフォルト実装をJavaのinterfaceのdefaultメソッドとしても扱えるようになります。
めでたしめでたし、と思いきや!JvmDefaultはdeprecatedになる予定なので今後は使わないほうが良さそうです。
2020/12現在、まだexperimentalですがJvmDefaultWithoutCompatibilityが1.4から登場しています。-Xjvm-default=allと-Xjvm-default=all-compatibilityというモードも増えています。
これはとてもややこしい話なんですが、後方互換を保つためコードの場合、-Xjvm-default=all-compatibilityを選んだほうが良いです。新規プロダクトとかなら-Xjvm-default=allが良いです。そしてJvmDefaultWithoutCompatibilityアノテーションは互換性が不要であることをマークするためのアノテーション。うーん、ややこしや。詳細は以下。
https://blog.jetbrains.com/kotlin/2020/07/kotlin-1-4-m3-generating-default-methods-in-interfaces/
もう少し詳しく日本語駄文解説したほうが良い気もするので、もしかしたら別記事にまとめるかもしれません。
結局ユースケースとして多いのは
Jvmのつく以下のアノテーションです。
Javaとの相互運用性で困ったらこのあたりなんか調べて使えばいいんだなぁ、ぐらいの雑な感じで覚えておけば一旦は良いと思います。
- JvmStatic (staticメソッド使いたいとき)
- JvmField (staticフィールド使いたいとき)
- JvmName (Javaクラス名を指定したい時)
- JvmOverloads (うまいことオーバーロードさせたいとき)
- JvmDefault (deprecatedになる予定。Javaのinterfaceのデフォルトメソッド使いたいとき)
- JvmDefaultWithoutCompatibility(experimental。Javaのinterfaceのデフォルトメソッド使いたいとき)
- JvmMultiFileClass(Kotlinの複数ファイルにJavaクラス名指定したいとき)
- JvmSuppressWildcards(ジェネリクス頑張るとき)
- JvmWildcard(ジェネリクス頑張るとき)
Javaのコンパイラが作るsyntheticメソッドを強制的に作るというものので
ほとんどニーズはないと思います。
https://stackoverflow.com/questions/41022275/whats-the-intended-use-of-jvmsynthetic-in-kotlin
君は合成フィールドを知っているか!(Java)
まとめ
- Kotlinは何も考えずデフォルト設定で使うとJava SE 6互換なことが多いよ
- Javaのinterfaceのデフォルトメソッド対応は途上だよ
- jvmtargetは本番環境のJDKバージョンと合わせるべし
- Javaとの相互運用性に困ったらJvmアノテーションを調べるべし
0 件のコメント:
コメントを投稿