KotlinのGenericsの変位 メモ

Kotlin in Actionを読みながら試しながらつぶやきまくって作っていたんですが、後から見返しにくいので自分用にまとめておきます。
reifiedとかfun \<T : Any>でnull非許容なGenericsが作れるとかをすでに知っている人用です。

変位とは型引数が異なる型同士がどのように関連しているかを表現するものらしいです。

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">KotlinのGenerics勉強してる。3秒で忘れそうなのでメモしていこう。<br>これは何となく最初から分かるけど&quot;サブタイプ&quot;はこういうときにStringはAnyのサブタイプ<br>    val str:String = &quot;aaa&quot;<br>    val any:Any = str</p>&mdash; takahirom (@new_runnable) <a href="https://twitter.com/new_runnable/status/1005400679584694272?ref_src=twsrc%5Etfw">2018年6月9日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


## サブタイプ

これは何となく最初から分かるけど"サブタイプ"はこういうときにStringはAnyのサブタイプ

```kotlin
    val str:String = "aaa"
    val any:Any = str
```

こういうことができちゃうと安全じゃなくなる。だから`MutableList<String>`は`MutableList<Any>`のサブタイプではない。

```kotlin
    val stringList = mutableListOf<String>("a", "b")
    val anyList:MutableList<Any> = stringList // 実際はエラーが出る
    anyList.add(12)
    stringList.sumBy { it.length }
```

## 不変

このようにMutableList<T>の場合、任意のTの型に関してサブタイプにならない。この場合MutableListは型パラメーターに関して"不変"と呼ばれる。

## 共変
MutableListではなく、Listの場合はAがBのサブタイプであれば、List\<A>がList\<B>のサブタイプになる。この場合"共変"と呼ばれる。

## 共変の作り方

| 不変になっている | outをつけて共変になっている |
|---|---|
| ![image.png](https://qiita-image-store.s3.amazonaws.com/0/27388/fc578b62-c935-0471-9af6-b1f70e6c206a.png) | ![image.png](https://qiita-image-store.s3.amazonaws.com/0/27388/7c046dc8-5703-873f-bc5e-3504dd80f613.png) |

outをつけると、共変にできる。

KotlinのListも同様にoutを使っているので共変になっている

```kotlin
interface List<out T> : Collection<T> {
  operator fun get(index: Int): T
}
```

## 共変の制限について
outをつけると、**Tを関数の返り値のところ(これをoutポジションという)のみで利用できる**という制限がつく。
MutableListではaddで引数にもTを取りgetで返り値にもTを取るので共変として宣言することができない。
outで宣言されていてもコンストラクタの引数にはTを使える

コンストラクタの引数であっってもvarである場合は、setterが作られるため、inポジションにTができてしまうので、利用することができない。
例外があってポジションのルールはpublic 、protected、internalにだけ適応されるので、private varならOK。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/27388/d5907361-b53f-ef38-1e19-f2017ece3b3f.png)

## 反変
反変は共変はinポジション、つまり引数でのみ利用することができ、サブタイプの関係は共変の逆になる。
つまり「BがAのサブタイプならば、Consumer\<A>はConsumer\<B>のサブタイプである」という関係になる。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/27388/23e509db-5323-0925-cbbb-1264d2cea012.png)

## 共変、反変のFunctionインターフェースでの応用
Functionインターフェースでは引数に反変、返り値に共変と両方使われている。
そのため`(Animal) -> Int`は`(Cat) -> Number`にキャストできる。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/27388/6e155baf-e6aa-f87a-7ba9-23b2df5c1119.png)

## 宣言場所変位指定と使用場所変位指定
これまで書いてきたクラス宣言に変位修飾子(in out)を指定するとクラスを使う場所で適応される。これを"宣言場所変位指定"という。

関数の引数などでもoutなどはつかえて、それは使用場所変位指定という。

| 使用場所変位指定なし | ![image.png](https://qiita-image-store.s3.amazonaws.com/0/27388/08e09b33-35e7-343c-08fa-b7b0cd448492.png)|
|---|---|
使用場所変位指定あり | ![image.png](https://qiita-image-store.s3.amazonaws.com/0/27388/db91a829-63a6-a4b1-2020-c178400e86ca.png)

## 投影
関数の引数の型やローカル変数の型、関数の戻り値に変位修飾子をつけることで起こる事を"型投影"という。
この場合、sourceは通常のMutableListではなく、"投影された"(制限された)MutableListとなる。制限されている例は2枚目

投影の例 (使用場所変位指定と同じ)
![image.png](https://qiita-image-store.s3.amazonaws.com/0/27388/203ac002-4cad-346c-7811-8f56f6edd438.png)

outに投影されている(制限されている)のでadd()できない。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/27388/ebb70c1c-1761-57c9-c37c-81830e772232.png)

## スター投影
スター投影はAny?と違い、特定の型を意味する。そのためMutableList<*>にMutableList<Number>とかを代入できる。
しかし、それを変更できちゃうと安全じゃなくなるので、out Any?に投影される(同じように機能する)。
getはできるがaddはできない。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/27388/6f5c1754-5e9c-3139-1837-8b4424bb6fe7.png)

## まとめ
言葉は難しいけど、試しながらやったらなんとなくわかったので良かったです。Kotlin in Actionはおすすめです。