この記事は、以下を基本的に引用した内容となっております。
一部翻訳がわかりづらい部分があったため、自分なりにわかりやすく修正した内容となっております。
ジェネリクス - Kotlin Programming Language
記事を読む前に
この記事の内容を理解するためには、ジェネリクスについて知っておく必要があります。
以下サイトが初心者にもわかりやすくまとめられていました。
JavaジェネリックスのPECS原則、extendsとsuperの勘所 -- ぺけみさお
Javaジェネリクス:共変、反変、非変(これ以上簡単にはならない) | GWT Center
この辺について把握していないと、以下の話についていけないかと思います。。
宣言箇所変位
ジェネリックインターフェイスのSource<T>
があると仮定します。また、パラメータとしてT
を返すメソッドのみを持つとします。
// Java
interface Source<T> {
T nextT();
}
それはSource<Object>
型の変数(呼び出せるコンシューマメソッドがない)内でSource<String>
のインスタンスへの参照を保持するのに完全に安全です。 – しかし、Javaはこれを知っているし、まだそれを禁止していません:
// Java
void demo(Source<String> strs) {
Source<Object> objects = strs; // !!! Java では許可されていない
// ...
}
これを修正するために、Source<? extends Object>
型のオブジェクトを宣言する必要があります。全ての同メソッドを前のような変数で呼ぶことができるので、順序に意味はなく、より複雑な型で追加することに価値はありません。しかし、コンパイラはそれを知りません。
Kotlinでは、コンパイラにこの種の問題を説明する方法があります。これは、 宣言箇所変性と呼ばれています:ソースの 型パラメータT
をSource<T>
のメンバからのみ 返し (プロデュースする)、消費されることがないということを確認するために、アノテーションを付けることができます。これを行うために、我々はout
修飾子を提供します。
abstract class Source<out T> {
abstract fun nextT(): T
}
fun demo(strs: Source<String>) {
val objects: Source<Any> = strs // これは OK 、なぜなら T はoutパラメータのため
// ...
}