アプリ開発備忘録

PlayStationMobile、Android、UWPの開発備忘録

KotlinのGenerics、in outについて理解する

Hoge<in T> とかはあまり使う機会が無い、あまり理解しないで使用していたので、これについてまとめます。

公式ドキュメントは以下。
https://kotlinlang.org/docs/generics.html#declaration-site-variance

in

Javaでは以下の様なStringをAnyにアップキャストの様な事をするのは許可されていないけど、Kotlinではできるというものです。

// Java
interface Source<T> {
    T nextT();
}

void demo(Source<String> strs) {
    Source<Object> objects = strs; // !!! Not allowed in Java
    // ...
}

Kotlinでの書き方。

interface Source<out T> {
    fun nextT(): T
}

fun demo(strs: Source<String>) {
    val objects: Source<Any> = strs // This is OK, since T is an out-parameter
    // ...
}

なんでこのわざわざoutと書かないといけないの?と思いましたので、outで縛られる引数での利用のコードを書いてみます。
元がStringなのにIntが入れられてしまうのでoutで縛っているわけですね。これのお陰で Source<String>Source<Any> に代入できるというわけです。

public interface Source<out T> {
    public fun nextT(): T
    public fun set(value: T) // Type parameter T is declared as 'out' but occurs in 'in' position in type T
}

public fun demo(strs: Source<String>) {
    val objects: Source<Any> = strs

    val int:Any = 0
    objects.set(int) // 元がStringなのにIntが入れられてしまう。
}

in

続いて、inのサンプルは以下です。outの時と違って、NumberをDoubleにアップキャストの様な事をする形になります。

interface Comparable<in T> {
    operator fun compareTo(other: T): Int
}

fun demo(x: Comparable<Number>) {
    x.compareTo(1.0) // 1.0 has type Double, which is a subtype of Number
    // Thus, you can assign x to a variable of type Comparable<Double>
    val y: Comparable<Double> = x // OK!
}

compareTo ではNumberを受け付けているので、DoubleだろうがIntだろうが元々受け取れます。これでoutのような戻り値で使用するようなコードを書いてしまうと、Number で定義しているのに、 Double を要求されてしまいます。

まとめ

outはより上位の型を返すことができる。これは戻り値の時はより上位の型を受け取って良いから。引数の時はダウンキャストになってしまうので制限されている。
inはより下位の値を受け取る事ができる。これは引数が上位の値を受け取れるように実装されているから。戻り値の時はより下位の型を要求されてしまうので制限されている。