Android 「この値は @NonNull やで」→ Kotlin 「ほな厳密なnullチェックするで」→ Android 「実行時にやっぱ null 返すわ!」→ NullPointerException
うそつくな
本題
Android
の@NonNull
アノテーションはたまによくnull
を渡す。これがJava
なら問題なかったけど、Kotlin
だと例外で落ちてしまうって話。
おことわり
本記事で言う@NonNull
はandroid.annotation
、androidx.annotation
のことです。
修正する
多分2パターン存在します。
Javaファイルを作成し、@NonNullなインターフェースを継承し、@NonNullを全部@Nullableにする
.java
を作る必要があるので、なんか負けた気分になります(が、Android
が@NonNull
守らないのが悪いので仕方ない、、、)
例えば、こんな感じにJavaで書かれたインターフェースに@NonNull
がついている場合
(以下はAndroid
のOnGestureListener
です)
自前でこのインターフェースを継承するインターフェースを作成し、@NonNull
を@Nullable
に置き換えます。
このときJavaで作成する必要があります。
これで、Kotlin
でもNullable
として扱ってくれるので、null
が入ってきた場合でも落ちなくなります。
MotionEvent
が全部Nullable
になりました。やったね
もう一つ
これはやっていいのかわからないのですが、どうしてもKotlin
で完結させたい場合は使えます。
Suppress
で黙らせるやつですね。引数がNonNull
からNullable
になるだけ(引数が増えているわけではない)なので、おそらく実行時に落ちることはないと思いますが、、、
事の発端
onCellInfoChanged
でNullPointerException
になってしまう。
原因はonCellInfoChanged
をAndroid
が呼び出す際にcellInfo
をnull
で渡しているため、、、
Androidの@NonNullアノテーション
これ単純にビルド時にnull
の可能性があるときに警告を出すものなので、ライブラリ開発者とかは意識するといいかもしれないですが、それ以外ならぶっちゃけ無くてもまあ、、、
一方 Kotlin の NonNull
Kotlin
はNull
を厳格にチェックするため、?
がついていない引数はビルドも通らないし、実行時も落ちるようになっています。
また実行時にもNonNull
は機能し、関数内に引数がnull
ではないことを確認する処理が自動で挿入されています。
(全然Androidとは関係ないKotlin
プロジェクトだけど変わらないはず)
逆コンパイルするとnullチェック
する関数が挿入されている
Javaで書いた@NonNullはKotlinだと?
さて、@NonNull
でも実行時はnull
を渡す可能性がある話をしましたが、、、いかが。
まぁ予想通りKotlin
でもアノテーションを尊重してNonNull
として扱われます。
https://kotlinlang.org/docs/java-interop.html#nullability-annotations
今回、@NonNull
がJava
の場合に落ちないけど、Kotlin
の場合にいきなり落ちるようになったのはこの影響です。
以上です。
おわりに
targetSdk = 33
から、MotionEvent
がNonNull
になった影響でわりとIssue
がちらほら(全然良くない;;)
ちなみに私が引っかかったonCellInfoChanged
に関してもnull
を返さないよう修正されたそうですが、古いバージョンには残り続けるでしょうね、、、
https://issuetracker.google.com/issues/237308373