回答:
共分散:
class Super {
Object getSomething(){}
}
class Sub extends Super {
String getSomething() {}
}
Super#getSomethingの戻り型のサブクラスを返すため、Sub#getSomethingは共変です(ただし、Super.getSomething()のコントラクトを完全に満たします)
反変
class Super{
void doSomething(String parameter)
}
class Sub extends Super{
void doSomething(Object parameter)
}
Sub#doSomethingは、Super#doSomethingのパラメーターのスーパークラスのパラメーターを取るため、反変です(ただし、Super#doSomethingのコントラクトを完全に満たします)。
注意:この例はJavaでは機能しません。Javaコンパイラは、doSomething()メソッドをオーバーロードし、オーバーライドしません。他の言語は、このスタイルの反変をサポートしています。
ジェネリック
これはジェネリックスでも可能です:
List<String> aList...
List<? extends Object> covariantList = aList;
List<? super String> contravariantList = aList;
これでcovariantList
、ジェネリックパラメーターをとらないすべてのメソッドにアクセスできます(「オブジェクトを拡張する」ものである必要があるため)。ただし、ゲッターは正常に動作します(返されるオブジェクトは常に「オブジェクト」型であるため)
反対の場合は当てはまりますcontravariantList
。ジェネリックパラメーターを使用してすべてのメソッドにアクセスできます( "String"のスーパークラスである必要があるため、常に1つ渡すことができます)。 )
共分散:IterableおよびIterator。ほとんどの場合、共変量Iterable
またはを定義することは理にかなっていますIterator
。Iterator<? extends T>
と同じように使用できIterator<T>
ます。typeパラメータが表示されるのはnext
メソッドからの戻りの型のみなので、に安全にアップキャストできますT
。ただし、S
extends がある場合は、型の変数にT
割り当てることもできます。たとえば、findメソッドを定義している場合:Iterator<S>
Iterator<? extends T>
boolean find(Iterable<Object> where, Object what)
List<Integer>
and 5
で呼び出すことはできないので、次のように定義することをお勧めします
boolean find(Iterable<?> where, Object what)
逆分散:コンパレータ。それはComparator<? super T>
ちょうど同じように使用できるので、ほとんど常に使用する意味がありますComparator<T>
。タイプパラメータはcompare
メソッドパラメータタイプとしてのみ表示されるため、T
安全に渡すことができます。たとえばDateComparator implements Comparator<java.util.Date> { ... }
、aがありList<java.sql.Date>
、そのコンパレーター(java.sql.Date
のサブクラスjava.util.Date
)でaをソートする場合、次のようにできます。
<T> void sort(List<T> what, Comparator<? super T> how)
ではなく
<T> void sort(List<T> what, Comparator<T> how)
リスコフの代替原理を見てください。実際、クラスBがクラスAを拡張する場合、Aが必要なときはいつでもBを使用できるはずです。
contra variant
言うまでもありません。super.doSomething("String")
に置き換えることができませんでした sub.doSomething(Object)
。