Javaの共分散と反分散の良い例を示してください。
共分散:
class Super {
Object getSomething(){}
}
class Sub extends Super {
String getSomething() {}
}
Sub#getSomethingは、Super#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()-Methodをオーバーライドしません。他の言語はこのスタイルの矛盾をサポートします。
ジェネリック
これはジェネリックでも可能です:
List<String> aList...
List<? extends Object> covariantList = aList;
List<? super String> contravariantList = aList;
ジェネリックパラメーターを受け取らないcovariantList
のすべてのメソッドにアクセスできるようになりました(mustは「オブジェクトを拡張する」必要があるため) 、しかし、ゲッターは正常に動作します(返されるオブジェクトは常に「Object」タイプであるため)
contravariantList
の場合は逆です:ジェネリックパラメーターを使用してすべてのメソッドにアクセスできます( "String"のスーパークラスでなければならないため、常に1つを渡すことができます)。 Stringの他のスーパータイプの)
共分散:IterableおよびIterator。共変Iterable
またはIterator
を定義することはほとんど常に意味があります。 Iterator<? extends T>
はIterator<T>
と同じように使用できます-型パラメーターが表示される場所はnext
メソッドからの戻り型のみであるため、T
に安全にアップキャストできます。ただし、S
がT
を拡張している場合は、Iterator<S>
をIterator<? extends T>
型の変数に割り当てることもできます。たとえば、findメソッドを定義している場合:
boolean find(Iterable<Object> where, Object what)
List<Integer>
および5
で呼び出すことはできないため、次のように定義する方が適切です。
boolean find(Iterable<?> where, Object what)
コントラバリアンス:コンパレータComparator<? super T>
と同じように使用できるため、Comparator<T>
を使用することはほとんど常に意味があります。 typeパラメーターはcompare
メソッドパラメータータイプとしてのみ表示されるため、T
を安全に渡すことができます。たとえば、DateComparator implements Comparator<Java.util.Date> { ... }
があり、List<Java.sql.Date>
をそのコンパレーターで並べ替える場合(Java.sql.Date
はJava.util.Date
のサブクラスです)、次のようにできます。
<T> void sort(List<T> what, Comparator<? super T> how)
ではなく
<T> void sort(List<T> what, Comparator<T> how)