108

请举一个Java中协变和逆变的好例子。

4

3 回答 3

159

协方差:

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() 方法。其他语言确实支持这种逆变方式。

泛型

这对于泛型也是可能的:

List<String> aList...
List<? extends Object> covariantList = aList;
List<? super String> contravariantList = aList;

您现在可以访问所有covariantList不采用通用参数的方法(因为它必须是“扩展对象”的东西),但 getter 可以正常工作(因为返回的对象将始终是“对象”类型)

反之亦然contravariantList:您可以使用泛型参数访问所有方法(您知道它必须是“String”的超类,因此您始终可以传递一个),但没有 getter(返回的类型可能是 String 的任何其他超类型)

于 2010-03-23T16:04:34.770 回答
49

协方差:Iterable 和 Iterator。Iterable定义协变体或几乎总是有意义的IteratorIterator<? extends T>可以用作Iterator<T>- 类型参数出现的唯一位置是next方法的返回类型,因此可以安全地向上转换为T. 但是如果你有S扩展T,你也可以分配Iterator<S>给一个类型的变量Iterator<? extends T>。例如,如果您要定义一个 find 方法:

boolean find(Iterable<Object> where, Object what)

您将无法使用List<Integer>and调用它5,因此最好将其定义为

boolean find(Iterable<?> where, Object what)

方差:比较器。使用它几乎总是有意义的Comparator<? super T>,因为它可以用作Comparator<T>. 类型参数仅作为compare方法参数类型出现,因此T可以安全地传递给它。例如,如果您有 aDateComparator implements Comparator<java.util.Date> { ... }并且您想List<java.sql.Date>使用该比较器对 a 进行排序(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)
于 2010-03-23T19:29:03.393 回答
-6

看看Liskov 替换原理。实际上,如果 B 类扩展了 A 类,那么您应该能够在需要 A 时使用 B。

于 2010-03-23T15:24:30.017 回答