Java開発者に送る、Kotlinのジェネリック

KotlinのジェネリックはJavaで表現可能な仕様と、そうでないものがあります。
まずは、Javaで表現可能なものから見ていきます。

# ジェネリック関数

Kotlinでジェネリック関数を宣言してみます。

```kotlin
// Kotlin
fun <T> convertToString(arg: T) : String {
    return arg.toString()
}
```

通常の関数宣言の`fun`キーワードと関数名の間に型パラメータを宣言します。
同等の関数をJavaで表現することが出来ます。

```java
// Java
<T> String convertToString(T arg) {
    return arg.toString();
}
```

## 型制約

型パラメータに制約を持たせることが出来ます。

```kotlin
fun <T: Exception> convertToString(arg: T) : String {
    return arg.localizedMessage
}
```

型パラメータ`T`はExceptionもしくはそのサブクラスとする制約を付加しました。
こちらもJavaで表現可能です。

```Java
// Java
<T extends Exception> String convertToString(T arg) {
    return arg.getLocalizedMessage();
}
```

# ジェネリッククラス

Kotlinでジェネリッククラスを定義してみます。

```kotlin
class Box<T>(t: T) {
    var value = t
}
```

クラス名とプライマリコンストラクタの間に型パラメータを宣言します。
同等のクラスをJavaで表現することが出来ます。

```java
class Box<T> {
    T value;
    Box(T t) {
        this.value = t;
    }
}
```

## 型制約

クラスについても型パラメータに制約を持たせることが出来ます。

```kotlin
class Box<T: Number> (t: T) {
    var value = t
}
```

型パラメータ`T`について、Numberもしくはそのサブクラスとする制約をつけました。
こちらもJavaで表現可能です。

```java
static class Box<T extends Number> {
    T value;

    Box(T t) {
        this.value = t;
    }
}
```

# `where`句

関数やクラスで型制約を複数指定する場合は`where`句が便利です。

```kotlin
fun <T> doSomething(t: T) where T: Runnable, T: Cancellable {
    
}
```

# 変数宣言と変位指定

Javaの境界ワイルドカード型と同じく、変数宣言時に変位を指定する記法がKotlinにもあります。

|| Java | Kotlin |
|:--|:--|:--|
| 上限境界制約 | <? extends XXX> | \<out XXX> |
| 下限境界制約 | <? super XXX> | \<in XXX> |

### 共変
DogはAnimalのサブタイプです。
そこで、List\<Dog>はList\<Animal>のサブタイプとなるように共変の関係を作ります。

```kotlin
open class Animal
class Dog: Animal()

var animals: List<out Animal> = emptyList()
val dogs: List<Dog> = emptyList()

animals = dogs
```

### 反変
DogはAnimalのサブタイプです。
List\<Animal>はList\<Dog>のサブタイプとなるように反変の関係を作ります。

```kotlin
open class Animal
class Cat: Animal()

val animals: MutableList<Animal> = mutableListOf()
var cats: MutableList<in Cat> = mutableListOf()

cats = animals
```

Kotlinでは、宣言時の変位指定を**型投影 (type projection)**と呼びます。

# クラス宣言と変位指定
Javaでは共変と反変の関係を、クラスそのものに指定することは出来ません。そのため、変数宣言などはその都度変位指定する必要があります。

一方、Kotlinにはクラスの宣言時に変位を指定する仕組みがあります。
クラスの設計者、実装者の意図に反して変位が利用されるリスクがなくなります。

```kotlin
//             ↓型パラメータにoutポジションを指定
class Supplier<out T> {
    fun get(): T {
        //
    }
}
```

`Supplier`クラスは型パラメータTについて共変となります。

```kotlin
var animalSupplier: Supplier<Animal> = Supplier()
var dogSupplier: Supplier<Dog> = Supplier()
animalSupplier = dogSupplier
```

変数の宣言時に変位指定が不要となります。
同様に`in`ポジションを指定することで反変とすることも可能です。

# 非null許容型とジェネリック

下記のジェネリック関数、引数にはnullを渡すことが出来ます。

```kotlin
fun <T> doSomething(t: T){
}

doSomething(null) // OK
```

Javaのルートクラス`Object`に相当するKotlinの型は`Any`です。
が、制約のない型パラメータの上限制約は`Any`ではなく`Any?`です。

もし、非null許容型のジェネリック関数を宣言したければ
型制約に`Any`を指定します。

```kotlin
fun <T: Any> doSomething(t: T){
}

doSomething(null) // コンパイルエラー
doSomething(1) // OK
doSomething("hoge") // OK
```

# スター投影

型パラメータにアスタリスク`*`を指定するスター投影という仕組みがあります。

```kotlin
var animals: List<*> = //
```

上記の`List<*>`は`List<Any?>`と等値ではありません。`List<out Any?>`として振る舞います。
スター投影は型パラメータに関心がない場合に使う構文です。
要素を取得し`Any?`として扱う分には安全ですが、リストに要素を追加することは安全ではありません。

下記のように、想定外の型を受け取るリスクがあるため`out`ポジションとして振る舞います。

```kotlin
val animals: MutableList<*> = mutableListOf<Animal>()
val animal = animals.first() // OK
animals.add(1) // コンパイルエラー
animals.add(Dog()) // コンパイルエラー
```