【!ってなんだ】KotlinとJava、nullとPlatformType【NullableにNotNull】

　この投降は、[Kotlin Advent Calendar 2016](http://qiita.com/advent-calendar/2016/kotlin)の5日目の投稿です。

# はじめに

　Kotlinではnullをより扱いやすくするように、Nullable、「`?.`」そして「`?:`」など様々な言語機能・構文が提供されています。

　Kotlinのみで書かれたコードでは、`NullPointerException`に遭遇することはそうそうないでしょう。

　ところが、実際の開発ではではKotlinだけで製品を完成させることはまずできません。Javaで書かれたSDKや外部ライブラリを利用して製品を作っていくことがほとんどです。

　その場合、

* Javaで定義されたメソッドの返り値を変数に代入する
* Javaで定義された抽象クラスを継承する
* Javaで定義されたインターフェースをSAM変換を用いて処理

　を行った場合、nullはどう扱われるのでしょうか？

　「Kotlinはnull安全だから」といって扱い方を間違えると、`NullPointerException`や`IllegalStateException`を発生させてしまいます。

　本投稿では、『【!ってなんだ】KotlinとJava、nullとPlatformType【NullableにNotNull】』と題して

　JavaとKotlinの相互運用において、大事なポイントとなるnullと`Platform Type`とその関連事項について紹介します。

# KotlinのNullableの確認

　まずは、KotlinのNullableについて確認しましょう。

## Stringを返すメソッドの例

　次のように、String型を返すメソッドを定義します。

```kotlin
fun returnString(): String = ""
```

　このメソッドの返り値を変数に代入しましょう。

　次のように変数の型を明示しない場合は変数`str`はString型と推論されます。String型の変数`str`で、`?.`を使ってメンバにアクセスした場合、「`?.`は必要ない」という旨の警告が出ます。

```kotlin
val str = returnString()
println(str.length)
// 次は?.は必要ないという旨の警告がでる
// println(str?.length)
```

　次のように変数の型を明示することも可能です。

```kotlin
val str: String = returnString()
println(str.length)
// 次は?.は必要ないという旨の警告がでる
// println(str?.length)
```

　String型を、String?型の変数に代入することも可能です。変数`str`は、String?と明示的に型を宣言しました。`str`はString?型なので、メンバにアクセスする際は、「`.`」ではなくて「`?.`」としないとコンパイルエラーになります。

```kotlin
// String?にStringを代入することもできる
val str: String? = returnString()
println(str?.length)
// println(str.length)は、コンパイルエラー
```

## String?を返すメソッドの例

　次は、String?型を返すメソッドを確認します。

```kotlin
fun returnNullableString(): String? = null
```

　このメソッドの返り値を変数に代入します。

　次のように変数の型を明示しない場合は変数`str`はString?型と推論されます。メンバにアクセスする際は、「`?.`」を使わないとコンパイルエラーになります。

```kotlin
val str = returnNullableString()
println(str?.length)
// println(str.length)は、コンパイルエラー
```

　次のようにString?型の返り値をString型の変数に代入することはできません。Type Mismatchでコンパイルエラーとなります。


```kotlin
// Type Mismatchでコンパイルエラー
// val str: String = returnNullableString()
```

　次のように変数の型を明示することも可能です。

```kotlin
val str: String? = returnNullableString()
println(str?.length)
// println(str.length)は、コンパイルエラー
```

　Kotlinで定義したメソッドをKotlinで使う例を確認しました。

　つぎは、Javaで作ったメソッドをKotlinで使う場合を示します。

# JavaのメソッドをKotlinから呼ぼう

　Javaで次のようなクラス・メソッドを定義します。

```java
public class Utility {
    public static String returnStringJava() {
        return null;
    }
}
```

　この「`Utility#returnStringJava`」をKotlinから呼び出したらどうなるでしょうか。

　ここでは、次のような順番で確認していきましょう。

1. String?型と型を明示した変数に代入した場合
2. String型と型を明示した変数に代入した場合
3. 型を明示しない変数に代入した場合

## String?型と明示した変数に代入した場合

　次のコードは、Javaで定義した`Utility#returnStringJava`を、String?型の変数に代入したコードです。

　String?型なので、メンバにアクセスするには`.`ではなくて`?.`でないとコンパイルエラーになります。

```kotlin
val str: String? = Utility.returnStringJava()

println(str?.length)

// println(str.length)は、コンパイルエラー
```

　Kotlinの中でのString?はnullかもしれない型、Stringはnullではない型です。

　一方で、JavaのStringはnullな可能性があります。

　Javaで書いたStringを返すメソッドの返り値を、KotlinではString?の変数に代入すれば扱いやすくなり、`NullPointerException`を回避できそうですね。

## String型と明示した変数に代入した場合

　次のコードは、Javaで定義した`Utility#returnStringJava`を、String型の変数に代入したコードです。

```kotlin
val str: String = Utility.returnStringJava()

// ?.は必要ないという旨の警告がでる
println(str?.length)

println(str.length)
```

　このコードは、コンパイルエラー`には`なりません。（「?.は必要ないという」旨の警告は出ますが。）コンパイルエラー`には`なりませんが、実行時エラーになります。どこで実行時エラーになるか想像してみてください。

　ちなみに「`Utility#returnStringJava`」は次のようにnullを返すメソッドでしたね。

```java
public class Utility {
    public static String returnStringJava() {
        return null;
    }
}
```

　先ほどのコードは、

```
val str: String = Utility.returnStringJava()
```

　で、nullをString型の変数に代入しようとしたタイミングで、`IllegalStateException`が発生して実行時エラーになります。代入しようとしたタイミングで`IllegalStateException`実行時エラーですよ！メンバにアクセスした時点で`NullPointerException`では`ありません`。

　ところでもし、「`Utility#returnStringJava`」が


```java
public class Utility {
    public static String returnStringJava() {
        return "";
    }
}
```

　のようにnullを返さず、非nullを返す場合は実行時エラーにはなりません。

## 型を明示しない変数に代入した場合

　JavaでStringを返すメソッドをKotlinで、

* 「String?型と明示した変数に代入した場合」
* 「String型と明示した変数に代入した場合」

　をみてきました。それでは、型を明示しない変数に代入した場合をみてみましょう。


```kotlin
val str = Utility.returnStringJava()

println(str.length)

println(str?.length)
```

　このコードは、コンパイルエラーにはなりません。警告も出ません。

　今までと違う点に「おや」と思うことはありませんか？

　strがString型の変数と明示的に宣言される、もしくは推論された場合、「`?.`」でのメンバアクセスは警告が出ていましたね。

　strがString?型の変数と明示的に宣言される、もしくは推論された場合、「`.`」でのメンバアクセスはコンパイルエラーになっていましたね。

　そのどちらでもありません。

　それは「Javaで定義・宣言された参照型を、Kotlinでは`Platform Type`型という特別な型で扱われるため」です。

```kotlin
val str = Utility.returnStringJava()
```

　上記のコードでstrは、StringともString?とも明示的に宣言されていません。

　strは、String?でもStringでもない型、`Platform Type`の`String!`に推論されているのです。

　`Platform Type`について詳しいことは次の節で紹介するとして、先に実行結果を見てみましょう。

　コードを再掲します。


```java
public class Utility {
    public static String returnStringJava() {
        return null;
    }
}
```

```kotlin
val str = Utility.returnStringJava()

println(str.length)

println(str?.length)
```

　を実行すると、`NullPointerException`が発生します。「`.`」でメンバにアクセスしたタイミングでです。

```kotlin
val str:String = Utility.returnStringJava()
```

　とString型であることを明示した変数では、変数への代入したタイミングで`IllegalStateException`を発生していましたね。

　例外が発生するタイミングと発生する例外が違うことに注目してください。

　もし、`Utility#returnStringJava`が非nullなStringを返す場合、

```java
public class Utility {
    public static String returnStringJava() {
        return "";
    }
}
```

　次のように`NullPointerException`は発生せず、正常にプログラミングが終了します。

```
0
0
```

# PlatformTypeって？

　それでは、`PlatformType`について説明します。

　Javaの参照型はnullになりえます。Kotlinだけで閉じていれば、Nullableか非Nullableかは明確で扱いやすいのですが、Javaとの境界がやっかいです。どんな参照型もnullかもしれませんし、そうではないかもしれませんから。

　Kotlinでは、(`@Nullable`と`@NotNull`が付いていない)Javaの来た参照型は、すべて特別にPlatform Typeとして扱われます。

　Platform Typeは、T!と表現されます。例えば、String!とか、View!とかです。

```java
public class Utility {
    public static String returnStringJava() {
        return null;
    }
}
```

　というメソッドをKotlinから使おうとした場合、IDEAの予測変換では`String!`型を返すよという情報が表示されます。

```kotlin
val str = Utility.returnStringJava()
```

　また、IDEAなどでstrは`String!`型だという情報が示されます。

　`Utility#returnStringJava`の返り値の型は`String!`だし、strは`String!`型ですが、明示的に`String!`という型を変数の宣言等には使えません。次のコードはコンパイルエラーになります。

```kotlin
// コンパイルエラー！！！！
// val str: String! = Utility.returnStringJava()
```

　Platform Typeである、String!型は、NullableなString?でもあり非NullableなStringでもある型です。次のように、String?にもStringにも代入できます。

```kotlin
val str = Utility.returnStringJava() // 推論され、strはString!型
val strNullable: String? = str // strNullableはString?型
val strNotNull: String = str // strNotNullはString型
```

　ところで、Javaの参照型はnullになりえました。そのため、String!型をString?に代入するのは問題ないのですが、String型に代入する時は注意が必要です。

```
val str:String = Utility.returnStringJava() // IllegalStateExceptionが投げられる
```

　（実際はnullな）String!をnullを許さないStringに代入しています。このコードは、代入しようとしたタイミングでIllegalStateExceptionが発生するのでしたね。

　Platform Typeのまま扱う場合も注意が必要です。

```
val str = Utility.returnStringJava() // 推論され、strはString!型
println(str.length) // NullPointerException
```

　このコードは、コンパイルエラーにはなりませんが、NullPointerExceptionが発生しましたね。

　まとめると、

　『Xyz!と書いている型は、Javaから来た参照型でPlatform Type。NullableなXyz?型としても扱えるし、非NullableなXyz型としても扱える。Xyzとして扱った場合、例外が発生することがある』


　またここでは説明を省きましたが、「(Mutable)Collection<T>!」という`Platform Type`もあります。この説明はまたの機会に。

# KotlinだってNullPointerExceptionが発生する

　「Kotlinはnull安全」といっても、Javaで定義したメソッドのKotlinでの扱い方を誤ると、`NullPointerException`や`IllegalStateException`が発生します。

　どのような時に発生するか、確認しましょう。

## IllegalStateExceptionが発生しうる非Nullable型への代入

　非Nullable型で宣言した変数にnullを代入した場合、代入したタイミングで`IllegalStateException`が発生します。

　Javaで、String型を返す`Utility#returnStringJava`、

```java
public class Utility {
    public static String returnStringJava() {
        return null;
    }
}
```

　をKotlinで次のように使うと、

```kotlin
val str:String = Utility.returnStringJava()
```

　代入したタイミングで時点で`IllegalStateException`が発生します。

```
Exception in thread "main" java.lang.IllegalStateException: Utility.returnStringJava() must not be null
```

## NullPointerExceptionが発生しうるPlatform Type型のメンバアクセス

　PlatformTypeの型で、中身がnullだった場合のメンバアクセスにおいて、「`.`」を使った場合、`NullPointerException`が発生します。

　Javaで、String型を返す`Utility#returnStringJava`、

```java
public class Utility {
    public static String returnStringJava() {
        return null;
    }
}
```

　をKotlinで次のように使うと、

```kotlin
val str = Utility.returnStringJava() // Platform Typeとして扱われる
println(str.length) // この時点でNullPointerException
```

　メンバアクセスした`str.length`で、`NullPointerException`が発生します。

```
Exception in thread "main" java.lang.NullPointerException
```

# Javaの型をNullable・NotNull指定

　前節で示した通り、Javaで定義したメソッドをKotlinで呼び出した場合、使い方を誤ると`NullPointerException`や`IllegalStateException`がスローされる場合があります。

　これを避けることはできないでしょうか？Javaで定義したメソッドを、Kotlin側に「これはnullを返すかもしれないから、Nullable型として扱ってね！」と伝えられればよさそうですね。

　これは、`Nullableアノテーション`を用いることで可能になります。

## Nullableアノテーション

　次のように、Javaのメソッドを`org.jetbrains.annotations.Nullable`をつけて定義します。

```java
@Nullable
public static String returnStringJavaNullableAnnotation() {
    return null;
}
```

　これをKotlin側から使います。


```kotlin
// String?型と推論される
val str = Utility.returnStringJavaNullableAnnotation()
println(str?.length)

// コンパイルエラー
// println(str.length)
```

　Javaで`@Nullable`アノテーションがついた`Utility#returnStringJavaNullableAnnotation`の返り値型は、KotlinにおいてString?型になります。(String!型ではありません。)そのためstrはString?と推論されます。

　String?型なので、メンバアクセスする場合は「`?.`」を用いないといけません。「`.`」ではコンパイルエラーになります。

　次のようにString?と型を明示的に書くこともできます。

```
// String?と型を明示的に書いてもOK
// val str : String?= Utility.returnStringJavaNullableAnnotation()
```

　`Utility#returnStringJavaNullableAnnotation`の返り値はString?型ですので、String型に代入はできません。

　次のコードはコンパイルエラーになります。

```
// コンパイルエラーに Type Mismatch
// val str: String = Utility.returnStringJavaNullableAnnotation()
```

　このように、Javaのnullを返すかもしれないJavaメソッドに`Nullableアノテーション`を付与しておくことで、Kotlin側でNullable型として扱ってもらうことが可能になります。

## NotNullアノテーション

　nullかもしれない`Nullableアノテーション`の次は、nullではないことを表す`NotNullアノテーション`です。

　次のように、Javaのメソッドを`org.jetbrains.annotations.NotNull`をつけて定義します。

```java
@NotNull
public static String returnStringJavaNotNullAnnotation() {
    return "";
}
```

　これをKotlin側から使います。

```kotlin
// String型と推論される
val str = Utility.returnStringJavaNotNullAnnotation()
println(str.length)
// ?.も書けるけれど、必要ないよと警告がでる
// println(str?.length)
```

　Javaで@NotNullアノテーションがついた`Utility#returnStringJavaNotNullAnnotation`の返り値型は、KotlinにおいてString型になります。(String!型ではありません。)そのためstrはStringと推論されます。

　String型なので、「`.`」でメンバアクセスをすることができます。「`?.`」ともかけますが、警告がでます。

　次のようにStringと型を明示的に書くこともできます。

```kotlin
// Stringと型を明示的に書いてもOK
val str : String = Utility.returnStringJavaNotNullAnnotation()
```

　KotlinでString型のインスタンスをSting?の変数に代入できたように、`Utility#returnStringJavaNotNullAnnotation`の返り値をString?型の変数に代入することも可能ではあります。

```kotlin
// String型のインスタンスをString?の変数に代入できるので、次もOK
val str: String? = Utility.returnStringJavaNotNullAnnotation()
```

　ところで、`@NotNull`アノテーションがついたメソッドがnullを返した場合、`IllegalStateException`がスローされます。

## たくさんあるぞ、Nullable・NotNull

　さきほど、

* `org.jetbrains.annotations.Nullable`
* `org.jetbrains.annotations.NotNull`

　を紹介しました。

　ところで、読者のあなたはNullableやNotNullと聞いて違うアノテーションを想像しませんか？

　Androidや、Lombok、FindBugsなどさまざまなSDKやツール・ライブラリに、Nullable・NotNullというアノテーションが存在します。

　先ほど紹介した

* `org.jetbrains.annotations.Nullable`
* `org.jetbrains.annotations.NotNull`

　でなくても、後述するアノテーションを用いると、同様の効果を得ることができます。


`org.jetbrains.annotations.Nullable`と同様の効果が得られるアノテーション一覧

* org.jetbrains.annotations.Nullable
* android.support.annotation.Nullable
* com.android.annotations.Nullable
* org.eclipse.jdt.annotation.Nullable
* org.checkerframework.checker.nullness.qual.Nullable
* javax.annotation.Nullable
* javax.annotation.CheckForNull
* edu.umd.cs.findbugs.annotations.CheckForNull
* edu.umd.cs.findbugs.annotations.Nullable
* edu.umd.cs.findbugs.annotations.PossiblyNull

`org.jetbrains.annotations.NotNull`と同様の効果が得られるアノテーション一覧

* org.jetbrains.annotations.NotNull
* edu.umd.cs.findbugs.annotations.NonNull
* android.support.annotation.NonNull
* com.android.annotations.NonNull
* org.eclipse.jdt.annotation.NonNull
* org.checkerframework.checker.nullness.qual.NonNull
* lombok.NonNull

　あなたが使っているSDKやツールの中のNullable、NotNullをぜひ使ってみてください。

　これは今後増える可能性もあります。[公式ドキュメント](https://kotlinlang.org/docs/reference/java-interop.html#nullability-annotations)や[ソースコード](https://github.com/JetBrains/kotlin/blob/master/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/JvmAnnotationNames.kt)を参照してください。

# メソッドのオーバーライドは？

　Javaで定義した引数なし返り値Stringのメソッドを、Kotlinで使う例を見てきました。しかし、実際の開発で多いのはSDK内に定義されているJavaのクラスを継承することでしょう。

　ここでは、その場合を見ていきましょう。

　Javaで次のようなクラスを定義します。


```java
public class Bundle {}
```

```java
public class MyActivity {
    public void onCreate(Bundle savedInstanceState){
    }
}
```

　KotlinでこのMyActivityを継承して、onCreateメソッドをオーバーライドしましょう。

　このような書き方もできますし、

```kotlin
class MyKActivity : MyActivity(){
    override fun onCreate(savedInstanceState: Bundle) {
        super.onCreate(bundle)
    }
}
```

　このような書き方も可能です。

```kotlin
class MyKActivity : MyActivity(){
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(bundle)
    }
}
```

　違いは、onCreateの引数がBundle型がBundle?型という点ですね。

　引数がBundle型で書いた場合、savedInstanceStateがnullだったならば、IllegalStateExceptionがスローされます。

　JavaのMyActivityを次のように変えます。

```
public class MyActivity {
    public void onCreate(@Nullable Bundle savedInstanceState){
    }
}
```

　このように`@Nullable`を付与した場合、

```kotlin
class MyKActivity : MyActivity(){
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(bundle)
    }
}
```

　としか書けなくなります。

　実際、[`android.support.v7.app.AppCompatActivity#onCreate`](https://developer.android.com/reference/android/support/v7/app/AppCompatActivity.html#onCreate(android.os.Bundle))の引数savedInstanceStateには`@Nullable`が付与されています。そのため、Bundle?としか書けません。

# さいごに

　本投稿では、『【!ってなんだ】KotlinとJava、nullとPlatformType【NullableにNotNull】』と題して、JavaとKotlinの相互運用のポイントとなるPlatform Typeを紹介しました。


* なんとなく?をつけてた
* !って何か気になっていた
* なんで、Bundle!って書けないのかな
* NotNullやNullableアノテーションの大事さに気づいてなかった

　という方の、理解の助けになれば幸いです。

# 関連

* [公式ドキュメント : Calling Java code from Kotlin](https://kotlinlang.org/docs/reference/java-interop.html)
* [スライド : KotlinとJavaとnullのライトな話 by RyotaMurohoshi](https://speakerdeck.com/ryotamurohoshi/kotlintojavatonullfalseraitonahua)
* [ブログポスト : Kotlin M9で追加されたPlatform Typeの話 by ngsw_taro](http://taro.hatenablog.jp/entry/2014/10/16/233702)