Java中的不变性,协方差和逆变性

min*_*ins 6 java generics inheritance covariance contravariance

关于泛型的 Java 课程正在引导我出现方差概念.这让我有些头疼,因为我无法找到它的简单演示.

我已经在stackoverflow上阅读了几个类似的问题,但我发现它们对于Java学习者来说太难理解了.实际上问题在于对泛型的解释需要理解方差,并且证明了方差的概念在很大程度上依赖于泛型理解.

我有一些希望读到这个,但最后我分享了CR的感觉:

这个标题让我想起了学习广义相对论的日子. - CR 13年12月22日7:34

四个理论问题让我很困惑,我找不到好的和简单的解释.在这里他们是,我目前的部分理解(我担心专家将非常有乐趣阅读这个).

欢迎您提供纠正和澄清的帮助(请记住,这适用于初学者,而不是专家).

这种理解有问题吗?

  1. 什么是与编程相关的不变性/协方差/逆变?我最好的猜测是:
    • 这是面向对象编程中遇到的问题.
    • 在查看类和祖先中的方法参数和结果类型时,这必须要做.
    • 这用于方法覆盖重载的上下文中.
    • 这用于在方法参数的类型或方法返回类型与类本身的继承之间建立连接,例如,如果类D是类A的后代,我们可以对参数的类型和方法方法返回类型?
  2. 方差如何与Java方法相关?我最好的猜测是,给定两个A和D类,其中A是D的祖先,以及overhiden/overloaded方法f(arg):
    • 如果两个方法中的参数类型之间的关系与两个类之间的关系相同,则方法中的参数类型称为COVARIANT,具有类型,否则说:A和D中arg类型之间的继承是协变的继承A和D类.
    • 如果参数之间的关系REVERSES类之间的关系,则arg类型表示对类类型的CONTRAVARIANT,否则说:A和D中arg类型之间的继承与A类和D类的继承相反.
  3. 为什么方差理解对Java程序员如此重要?我的猜测是:
    • Java语言创建者已经实现了语言中的差异规则,这对程序员可以做什么有影响.
    • 规则规定覆盖/重载方法的返回类型必须与继承相反.
    • 另一条规则规定,覆盖/重载的参数类型必须与继承协变.
    • Java编译器检查方差规则是否有效,并相应地提供错误或警告.使用差异知识可以更轻松地解密消息.
  4. 过度训练和超负荷有什么区别?最佳的揣测:
    • 当参数和返回类型都是不变的时,方法会覆盖另一个方法.编译器将所有其他情况理解为重载.

Ing*_*ngo 5

这不是特定于OO,而是与某些类型的属性有关。

例如,使用函数类型

 A -> B                 // functional notation
 public B meth(A arg)   // how this looks in Java 
Run Code Online (Sandbox Code Playgroud)

我们有以下内容:

令C为A的子类型,D为B的子类型。

 B b       = meth(new C());  // B >= B, C < A
 Object o  = meth(new C());  // Object > B, C < A
Run Code Online (Sandbox Code Playgroud)

但以下操作无效:

 D d       = meth(new A());        // because D < B
 B b       = meth(new Object());   // because Object > A
Run Code Online (Sandbox Code Playgroud)

因此,要检查meth的调用是否有效,我们必须检查

  • 期望的返回类型是声明的返回类型的类型。
  • 实际参数类型是声明的参数类型的类型。

这是众所周知的和直观的。按照惯例,我们说一个函数的返回类型是协变和方法的参数类型是逆变的

对于像List这样的参数化类型,我们认为参数类型在Java等具有可变性的语言中是不变的。我们不能说C的列表就是A的列表,因为如果是这样,我们可以将A存储在C的列表中,这令调用者感到惊讶,后者仅假定列表中的C。但是,在Haskell这样的值是不可变的语言中,这不是问题。因为我们传递给函数的数据不能被突变,所以如果C A 子类型,则C 列表实际上就是 A 列表。(请注意,Haskell没有真正的子类型,但是具有“更多/更少多态”的相关概念“类型。)