栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

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

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

本质上,这些术语描述了类型转换如何影响子类型关系。也就是说,如果

A
B
是类型,
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() { ... }}

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



转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/470599.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号