まず第一に、共分散と反変性は Kotlin に固有のものではなく、Java と C# にもそのような概念があります。
kotlin言語では 、out は共分散を意味し、in は反変を意味します。kotlin 言語で out と in を理解するために、まず Java ジェネリックスを例として使用します。ジェネリックスを使用する必要があるのは、ジェネリックスを使用する必要があるのは、その利点はコンパイルできることです。常にチェックされ、すべてのキャストは自動的かつ暗黙的に行われます。
1. Java の ? extends T および ? super T
? T を拡張します
継承関係を持つ複数のクラスを作成し、テストクラスを作成する
public class People{}
public class Man extends People{}
public class Test<T extends People>{
public void run(List<T> aa){
}
public void run2(List<? extends T> aa){
}
public void run3(List<? super T> aa){
}
}
run メソッドを呼び出してみる
List<Man> list = new ArrayList<>();
Test<People> test = new Test<>();
test.run(list);
このときリストを渡すとコンパイルに失敗することが分かります ここで分析してみましょう: Test はジェネリッククラスです 使用する前は T は不確定です 使用した後は T が決まりますPeople; 実行するパラメータとしてリストを渡します。メソッドでは、List<People> aa= list と同等ですが、List<People> aa= list は true ではありません。Man は People から継承しますが、テストは People タイプのみを保存しますオブジェクト、リストは Man タイプのオブジェクトのみを保存します。テストはリストとは関係ありません。
run2 メソッドを呼び出してみる
List<Man> list = new ArrayList<>();
Test<People> test = new Test<>();
test.run2(list);
この時点で、test.run2(list); 行のコードがコンパイルされて合格していることがわかりました。ここで分析しましょう: run2 メソッドにリストをパラメータとして渡すことは、List<? extends People> aa= list と同等であり、これは true です。List<? extends People> aa は、コレクションに People および People サブクラス オブジェクトが格納されることを意味します。前のセッションは制限されており、リストには People サブクラスのオブジェクトが格納されているため、コードがコンパイルされて確立されます。上限のワイルドカード <? extends T> が extends キーワードで宣言され、パラメータが T である可能性があることを示します。 T サブカテゴリ。
? スーパーT
次の方法で呼び出してみてください
List<People> list2 = new ArrayList<>();
Test<Man> test2 = new Test<>();
test2.run(list2);
test2.run2(list2);
この時点で、run と run2 の両方でエラーが報告されていることがわかりました。その理由は、Test クラス オブジェクトをインスタンス化するときに、T が Man に置き換えられていたためです。test2 には People 型のデータのみが格納され、run メソッドと run2 メソッドのパラメーターには、のみが格納されます。 Man 型のデータなので、この 2 つは関係がないため、構文のコンパイルが間違っています。
次の呼び出しメソッドを試してください
List<People> list2 = new ArrayList<>();
Test<Man> test2 = new Test<>();
test2.run3(list2);
この時点で、test2.run3(list); この行のコードがコンパイルされてパスしていることがわかりました。run3 メソッドのパラメータは List<? super T> aa です。? super T は下限のワイルドカードであり、使用時にパラメータの型が T または T の親クラスに制限されることを意味します。aa には型 T のオブジェクトが格納され、 T の親クラス; Test をインスタンス化するプロセスで、Man は T に置き換えられます。People はたまたま Man の親クラスです。Test の run3 メソッドを呼び出して最初のパラメータを渡す場合、List<? super Man と同等です。 > aa= list なので、コンパイルは成功します。
2. Kotlin のアウトとイン
kotlin の原理は上記の Java の原理と似ています。
open class People
class Man : People()
class Test<T : People> {
fun run(aa: MutableList<T>) {}
fun run2(aa: MutableList<out T>) {}
fun run3(aa: MutableList<in T>) {}
}
呼び出し方法は以下の通りです。
val list: MutableList<Man> = mutableListOf()
val test: Test<People> = Test<People>()
test.run2(list)
val list2: MutableList<People> = mutableListOf()
val test2: Test<Man> = Test<Man>()
test2.run3(list2)
結論は:
kotlin の「out T」は、ワイルドカード型の前の項を制限する Java の「?extends T」に相当します。
kotlin の「in T」は Java の「?super T」に相当し、ワイルドカード型の次の項を制限します。