在Java中,举例说明在重载和覆盖的情况下展示协变和逆变的函数?

111

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

3个回答

161

协方差:

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”的超类)


81
第一个逆变性的例子在Java中不起作用。Sub类中的doSomething()是一个重载,而不是一个覆盖。 - Craig P. Motlin
16
确实,Java不支持在子类型中使用逆变参数。只支持方法返回类型的协变(就像第一个例子中的情况)。 - the_dark_destructor
很好的答案。协变对我来说看起来很合理。但是你能指出一段在JLS中描述逆变的段落吗?为什么会调用Sub.doSomething? - Mikhail
3
正如Craig所指出的那样,这并不是可行的。我认为这里涉及到覆盖和重载之间的冲突,而SUN公司选择了(一如既往)向后兼容的选项。因此在Java中,当覆盖一个方法时,无法使用逆变参数。 - Hardcoded

50

协变:Iterable 和 Iterator。定义一个协变的IterableIterator几乎总是有意义的。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.Datejava.util.Date 的子类),您可以执行以下操作:

<T> void sort(List<T> what, Comparator<? super T> how)

但不能与

<T> void sort(List<T> what, Comparator<T> how)

-7
看看里氏替换原则。实际上,如果类B继承自类A,那么在需要A的地方,你应该能够使用B。

8
这并没有回答问题,而且会误导人。完全有可能设计一个违反语义正确性、因此违反LSP的变体系统。 - Matt Whipple
1
这并不适用于“contra variant”。super.doSomething("String") 不能被 sub.doSomething(Object) 替换。 - zinking
1
这不是问题。 - OlivierTerrien

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接