请给出Java中协变和逆变的一个好例子。
协方差:
class Super {
Object getSomething(){}
}
class Sub extends Super {
String getSomething() {}
}
Sub#getSomething是协变的,因为它返回Super#getSomething的返回类型的子类(但仍符合Super.getSomething()的契约)。
< p > 逆变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;
现在,您可以访问的所有不带泛型参数的方法(因为它必须是“extends Object”类型),但getter方法将正常工作(因为返回的对象始终为“Object”类型)。
contravariantList
则相反:您可以使用所有带有泛型参数的方法(您知道它必须是“String”的超类,因此您可以始终传递一个),但没有getter方法(返回类型可能是任何其他“String”的超类)
协变:Iterable 和 Iterator。定义一个协变的Iterable
或Iterator
几乎总是有意义的。Iterator<? extends T>
可以像Iterator<T>
一样使用——类型参数出现的唯一位置是next
方法的返回类型,因此它可以安全地向上转型为T
。但是,如果你有S
扩展了T
,你也可以将Iterator<S>
分配给类型为Iterator<? extends T>
的变量。例如,如果你正在定义一个查找方法:
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)
super.doSomething("String")
不能被 sub.doSomething(Object)
替换。 - zinking