Answers:
协方差:
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
不带通用参数的方法(因为它必须是“扩展对象”),但是吸气剂可以正常工作(因为返回的对象始终为“对象”类型)
相反的情况是正确的contravariantList
:您可以使用泛型参数访问所有方法(您知道它必须是“ String”的超类,因此您始终可以传递一个),但是没有getter(返回的类型可以是String的任何其他超类型) )
协方差:Iterable和Iterator。定义协变量Iterable
或几乎总是有意义的Iterator
。Iterator<? extends T>
可以用作Iterator<T>
-类型参数出现的唯一位置是方法的返回类型next
,因此可以放心地转换为T
。但是,如果有S
扩展T
,也可以分配Iterator<S>
给type变量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>
。类型参数仅作为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)
看一下Liskov替代原理。实际上,如果B类扩展了A类,那么只要需要A,您就应该能够使用B。
contra variant
。super.doSomething("String")
无法用代替 sub.doSomething(Object)
。