当前位置: 首页 > 面试题库 >

Java用简单的语言解释协方差,不变性和相反性?

汪志业
2023-03-14
问题内容

今天,我读了一些有关Java中协方差,协方差(和不变性)的文章。我阅读了英文和德文的Wikipedia文章,以及其他来自IBM的博客文章和文章。

但是我对这些到底是什么还是有些困惑?有人说这与类型和子类型之间的关系有关,有人说与类型转换有关,有人说它用于确定方法是被重写还是被重载。

因此,我正在寻找一个简单的英语解释,它向初学者展示了协方差和逆方差(以及不变性)。加号是一个简单的例子。


问题答案:

本质上,这些术语描述了类型转换如何影响子类型关系。也就是说,如果AB是类型,f是类型转换,且子类型关系(即A ≤ B表示A是的子类型B),则

  • f是协变的,如果A ≤ B暗示f(A) ≤ f(B)
  • f是矛盾的,如果A ≤ B暗示f(B) ≤ f(A)
  • f 如果以上两个都不成立,则是不变的

让我们考虑一个例子。让f(A) = List哪里List声明

class List<T> { ... } 

是f协变,逆变还是不变?协变将表示a List<String>是的子类型List,相反,a List是的子类型,List<String>并且不变都不是另一个的子类型,即List<String>List<Object>是不可转换的类型。在Java中,后者是正确的,我们说(某种程度上是非正式的)泛型是不变的。

另一个例子。让f(A) = A[]。是f协变,逆变还是不变?也就是说,String []Object []的子类型,Object []String []的子类型,还是两者都不是子类型?(答案:在Java中,数组是协变的)

这仍然很抽象。为了更具体,让我们看一下在Java中哪些操作是根据子类型关系定义的。最简单的例子是分配。该声明

x = y;

仅在时才编译typeof(y) ≤ typeof(x)。也就是说,我们刚刚了解到

ArrayList<String> strings = new ArrayList<Object>();
ArrayList<Object> objects = new ArrayList<String>();

不会用Java编译,但是

Object[] objects = new String[1];

将。

子类型关系很重要的另一个示例是方法调用表达式:

result = method(a);

非正式地说,该语句是通过将a方法的值分配给方法的第一个参数,然后执行方法的主体,然后将方法的返回值分配给来评估的result。就像最后一个示例中的普通分配一样,“右侧”必须是“左侧”的子类型,即,仅当typeof(a) ≤ typeof(parameter(method))和时,此语句才有效returntype(method) ≤ typeof(result)。也就是说,如果方法通过以下方式声明:

Number[] method(ArrayList<Number> list) { ... }

以下任何表达式都不会编译:

Integer[] result = method(new ArrayList<Integer>());
Number[] result = method(new ArrayList<Integer>());
Object[] result = method(new ArrayList<Object>());

Number[] result = method(new ArrayList<Number>());
Object[] result = method(new ArrayList<Number>());

将。

子类型很重要的另一个示例是重载。考虑:

Super sup = new Sub();
Number n = sup.method(1);

哪里

class Super {
    Number method(Number n) { ... }
}

class Sub extends Super {
    @Override 
    Number method(Number n);
}

非正式地,运行时会将其重写为:

class Super {
    Number method(Number n) {
        if (this instanceof Sub) {
            return ((Sub) this).method(n);  // *
        } else {
            ... 
        }
    }
}

为了编译标记行,覆盖方法的方法参数必须是覆盖方法的方法参数的超类型,返回类型必须是覆盖方法的子类型。从形式上讲,f(A) = parametertype(method asdeclaredin(A))必须f(A) = returntype(method asdeclaredin(A))至少是协变的,如果必须至少是协变的。

请注意上面的“至少”。这些是任何合理的静态类型安全的面向对象的编程语言都将强制执行的最低要求,但是编程语言可能会选择更加严格。对于Java 1.4,在覆盖方法(即parametertype(method asdeclaredin(A)) = parametertype(method asdeclaredin(B)),覆盖)时,参数类型和方法返回类型必须相同(类型擦除除外)。从Java 1.5开始,重写时允许使用协变返回类型,即以下内容将在Java 1.5中进行编译,但在Java 1.4中不进行编译:

class Collection {
    Iterator iterator() { ... }
}

class List extends Collection {
    @Override 
    ListIterator iterator() { ... }
}

我希望我覆盖了所有内容,或者更确切地说,是划伤了表面。我仍然希望它将有助于理解类型差异的抽象但重要的概念。



 类似资料:
  • 问题内容: 谁能解释我,编程语言理论中协变和逆变的概念吗? 问题答案: 协方差 非常简单,最好从某些收集类的角度来考虑。我们可以 参数化 的一些类型参数类。也就是说,我们的列表包含some 类型的元素。如果,列表将是协变的 S是Tiff列表的子类型List [S]是List [T]的子类型 (在这里,我使用数学定义 iff 表示 当且仅当 。) 也就是说,a 是一个 。如果有一些例程接受a 作为参

  • 问题内容: 请以简单的语言向我解释。请不要发布网络链接,因为我需要阅读开发人员而不是作者的理解。 问题答案: 我能为您指出的最好是SQL Join的直观说明。 这些图表对我有很大帮助。 从此处的链接文章中添加主要图表。 内部联接 内部联接仅生成在表A和表B中都匹配的记录集。 完全外部联接 完全外部联接将生成表A和表B中所有记录的集合,并在可用时提供双方的匹配记录。如果没有匹配项,则缺少的一面将为空

  • 主要内容:ANCOVA分析,比较两个模型我们使用回归分析来创建描述预测变量变量对响应变量的影响的模型。有时,如果我们有类似于是/否或男/女等值的分类变量,简单回归分析为分类变量的每个值提供多个结果。在这种情况下,可以通过使用分类变量和预测变量来研究分类变量的影响,并比较分类变量的每个级别的回归线。 这样的分析被称为协方差分析,也称为ANCOVA。 输入数据 从R提供的数据集创建一个包含字段,和的数据框。 这里我们将作为响应变量,将作为预

  • 本文向大家介绍协方差与相关性的区别?相关面试题,主要包含被问及协方差与相关性的区别?时的应答技巧和注意事项,需要的朋友参考一下 相关性是协方差的标准化格式,协方差本身很难做比较,例如,如果我们计算工资和年龄的协方差,因为这两个变量有不同的度量,所以我们会得到不能做比较的不同的协方差,为了解决这个问题,我们计算相关性来得到一个介于-1和1之间的值,就可以忽略它们各自不同的度量

  • 问题内容: 过去,我使用C 作为编程语言。我知道用C 编写的代码会经过编译过程,直到成为目标代码“机器代码”为止。 我想知道Java在这方面如何工作。用户编写的Java代码如何由计算机运行? 问题答案: Java实现通常使用两步编译过程。Java编译器将Java源代码编译为字节码。字节码由Java虚拟机(JVM)执行。现代JVM使用称为即时(JIT)编译的技术将字节码编译为硬件CPU在运行时即时理

  • 问题内容: 请显示一个有关Java中协方差和逆方差的好例子。 问题答案: 协方差: Sub#getSomething是协变的,因为它返回Super#getSomething的返回类型的子类(但完全填充了Super.getSomething()的协定) 逆差 Sub#doSomething是互变的,因为它采用了Super#doSomething的参数的超类的参数(但是,再次填充了Super#doSo