variance in kotlin

# variance in kotlin
## varianceとは
> パラメータ化されたクラス間の関係を表す
> Variance describes the relationship between parameterized classes
> [Idiomatic Kotlin: Variance](https://medium.com/tompee/idiomatic-kotlin-variance-82355d9a71df)

パラメータ化されたクラスとは、`List<MyClass>`における`MyClass`。

## covariant, contravariant and invariant
> 共変 (covariant): 広い型（例：double）から狭い型（例：float）へ変換すること。
> 反変 (contravariant) : 狭い型（例：float）から広い型（例：double）へ変換すること。
> 不変 (invariant): 型を変換できないこと。
[共変性と反変性 (計算機科学)](https://ja.wikipedia.org/wiki/共変性と反変性_(計算機科学))

kotlinでは、デフォルトだと、
パラメータ化されたクラス間の関係は不変（型を変換できない）となる。

例えば、
`List<String>`を例にとると、
kotlinでは、`String`は`Any`の派生型であるが
`List<String>`は、`List<Any>`へ変換できない。

## kotlinにおける共変と反変

kotlinでは、`out`と`in`キーワードを使って、共変と反変を表す。
`out`と`in`を使わない場合は、不変となる。

`List<String>`を、`List<Any>`へ代入しようとすると、
以下の通り、`type mismatch`によりcomplie errorが発生する。

```kotlin
val ls = mutableListOf("String")
val la: MutableList<Any> = ls // Type mismatch

// 上のコードのコンパイルが通ると、
// 下記の通り、int型の変数を、MutableList<String>に代入できることになる
// ∵ MutableList<String>はMutableList<Any>に代入できるため
ls.add(42)
```

### `MutableList<Any>`へ`String`を追加

```kotlin
fun <T> addAll(list1: MutableList<T>,
               list2: MutableList<T>) {
  for (elem in list2) list1.add(elem)
}

// MutableList<String>
val ls = mutableListOf("A String")
// MutableList<Any>
val la: MutableList<Any> = mutableListOf()

/**
* Type inference failed: 
* Cannot infer type parameter T in 
* fun <T> addAll(list1: MutableList<T>,list2: MutableList<T>)
*/
addAll(la, ls)  // compile error
```

型`T`が推量できないためエラーとなっている。

```
None of the following substitutions
(MutableList<Any>,MutableList<Any>)
(MutableList<String>,MutableList<String>)
can be applied to
(MutableList<Any>,MutableList<String>)
```

上のエラーメッセージのように、
引数で渡している`Any`と`String`に型`T`が合致していない。
これは、型`T`が不変であるからに他ならない。


### `out`を用いて型を共変化

```kotlin
fun <T> addAll(list1: MutableList<T>,
               list2: MutableList<out T>) {
    for (e in list2) list1.add(e)
}

// MutableList<String>
val ls = mutableListOf("String")
// MutableList<Any>
val la: MutableList<Any> = mutableListOf()
val f = addAll(la, ls)
```

上記のコードはcompile errorが発生しない。
list2のMutableListの型`T`に`out`を付与したことで、
list2が、型`T`（ここでは`Any`)において共変(AnyからStringへ変換)となる。

### `out`が使用できる条件
なぜ`out`を付与することで、共変となるのか。

> Covariance is read-only and useful as a return type (out)
> 共変は、読み取り専用で、戻り値の型として有効
> [Idiomatic Kotlin: Variance](https://medium.com/tompee/idiomatic-kotlin-variance-82355d9a71df)

`MutableList<E>`の定義を見てみる。

```kotlin
/**
 * A generic ordered collection of elements that supports adding and removing elements.
 * @param E the type of elements contained in the list. The mutable list is invariant on its element type.
 */
public interface MutableList<E> : List<E>, MutableCollection<E> {
    override fun add(element: E): Boolean
    override fun remove(element: E): Boolean
    override fun addAll(elements: Collection<E>): Boolean
    public fun addAll(index: Int, elements: Collection<E>): Boolean
    override fun removeAll(elements: Collection<E>): Boolean
    override fun retainAll(elements: Collection<E>): Boolean
    override fun clear(): Unit
    public operator fun set(index: Int, element: E): E
    public fun add(index: Int, element: E): Unit
    public fun removeAt(index: Int): E
    override fun listIterator(): MutableListIterator<E>
    override fun listIterator(index: Int): MutableListIterator<E>
    override fun subList(fromIndex: Int, toIndex: Int): MutableList<E>
}
```

上記のコメントにあるように、型`E`は、リストに含まれる要素の型であり、
mutable listはその型に関して不変(型を変換できない)である。

先ほどの`addAll`で使用している`list2`の関数`in`は、
`override fun iterator(): MutableIterator<E>`で、
戻り値に型`E`が指定されている。

これにより、`out`を付与することで、`list2`の型`E`に対して共変が成立する。

つまり、`addAll`内で使用している`list2`の関数は、戻り値にのみ`E`を指定しているため、
`Any`から`String`へ変換し、それを戻り値として返すことに矛盾が生じない。

一方で、`list1`の関数`add`は、
`override fun add(element: E): Boolean`で、
引数に型`E`が指定されている。

そのため、`list1`に`out`を付与することはできない。

```kotlin
fun <T> addAll(list1: MutableList<out T>, /* MutableList<String> */
               list2: MutableList<T> /* MutableList<Any> */) {
    for (e in list2) list1.add(e)
}


val ls = mutableListOf("String")
val la: MutableList<Any> = mutableListOf()
val f = addAll(ls, la)
```

ここで共変を許容すると、
`Any`から`String`へ変換した要素`E`を`MutableList<Any>`に追加できることになる。

### `in`は`out`の逆
> Contravariance is write-only and useful as an input type (in)
> 反変は、書き込み専用で、引数の型として有効
> [Idiomatic Kotlin: Variance](https://medium.com/tompee/idiomatic-kotlin-variance-82355d9a71df)

先ほどの例でいうと、`list1`に`in`を指定することでコンパイルが通る。

```kotlin
fun <T> addAll(list1: MutableList<in T>, /* MutableList<String> */
               list2: MutableList<T> /* MutableList<Any> */) {
    for (e in list2) list1.add(e)
}


val ls = mutableListOf("String")
val la: MutableList<Any> = mutableListOf()
val f = addAll(ls, la)
```

`list1`の関数`add`は、引数に`E`が指定されているため、
`String`から`Any`へ変換し、`MutableList`に追加することに矛盾は生じない。

これにより、反変が成り立つ

# Reference
[The Joy of Kotlin](https://www.manning.com/books/the-joy-of-kotlin)