用简单的英语解释协变、不变和逆变? [英] Covariance, Invariance and Contravariance explained in plain English?

查看:31
本文介绍了用简单的英语解释协变、不变和逆变?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

今天,我阅读了一些关于 Java 中协变、逆变(和不变性)的文章.我阅读了英文和德文维基百科文章,以及来自 IBM 的其他一些博客文章和文章.

Today, I read some articles about Covariance, Contravariance (and Invariance) in Java. I read the English and German Wikipedia article, and some other blog posts and articles from IBM.

但我仍然对这些到底是关于什么有点困惑?有人说它是关于类型和子类型之间的关系,有人说它是关于类型转换的,有人说它是用来决定一个方法是否被覆盖或重载.

But I'm still a little bit confused on what these exactly are about? Some say it's about relationship between types and subtypes, some say it's about type conversion and some say it's used to decide whether a method is overridden or overloaded.

所以我正在寻找一个简单的英语解释,向初学者展示什么是协方差和逆变(和不变性).一个简单的例子的加分点.

So I'm looking for an easy explanation in plain English, that shows a beginner what Covariance and Contravariance (and Invariance) is. Plus point for an easy example.

推荐答案

有人说它是关于类型和子类型之间的关系,有人说它是关于类型转换的,还有人说它是用来决定一个方法是否被覆盖或重载.

Some say it is about relationship between types and subtypes, other say it is about type conversion and others say it is used to decide whether a method is overwritten or overloaded.

以上所有.

本质上,这些术语描述了子类型关系如何受到类型转换的影响.即如果 AB 是类型,f 是类型转换,且≤ 子类型关系(即 A ≤ B 表示 AB 的子类型),我们有

At heart, these terms describe how the subtype relation is affected by type transformations. That is, if A and B are types, f is a type transformation, and ≤ the subtype relation (i.e. A ≤ B means that A is a subtype of B), we have

  • f 是协变的,如果 A ≤ B 意味着 f(A) ≤ f(B)
  • f 是逆变的,如果 A ≤ B 意味着 f(B) ≤ f(A)
  • f 是不变的,如果以上都不成立
  • f is covariant if A ≤ B implies that f(A) ≤ f(B)
  • f is contravariant if A ≤ B implies that f(B) ≤ f(A)
  • f is invariant if neither of the above holds

让我们考虑一个例子.让 f(A) = List 其中 List

Let's consider an example. Let f(A) = List<A> where List is declared by

class List<T> { ... } 

f 是协变的、逆变的还是不变的?协变意味着 ListList 的子类型,而 ListList 的子类型List 和不变量都不是另一个的子类型,即 ListList 是不可转换的类型.在 Java 中,后者是正确的,我们说(有点非正式)泛型是不变的.

Is f covariant, contravariant, or invariant? Covariant would mean that a List<String> is a subtype of List<Object>, contravariant that a List<Object> is a subtype of List<String> and invariant that neither is a subtype of the other, i.e. List<String> and List<Object> are inconvertible types. In Java, the latter is true, we say (somewhat informally) that generics are invariant.

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

Another example. Let f(A) = A[]. Is f covariant, contravariant, or invariant? That is, is String[] a subtype of Object[], Object[] a subtype of String[], or is neither a subtype of the other? (Answer: In Java, arrays are covariant)

这还是比较抽象的.为了更具体,让我们看看 Java 中的哪些操作是根据子类型关系定义的.最简单的例子是赋值.声明

This was still rather abstract. To make it more concrete, let's look at which operations in Java are defined in terms of the subtype relation. The simplest example is assignment. The statement

x = y;

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

will compile only if typeof(y) ≤ typeof(x). That is, we have just learned that the statements

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

不会在 Java 中编译,但是

will not compile in Java, but

Object[] objects = new String[1];

会.

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

Another example where the subtype relation matters is a method invocation expression:

result = method(a);

通俗地说,这个语句的计算方式是将a的值赋给方法的第一个参数,然后执行方法体,然后将方法的返回值赋给result.就像上一个例子中的普通赋值一样,右手边"必须是左手边"的子类型,即这个语句只有在 typeof(a) ≤ typeof(parameter(method)) 时才有效returntype(method) ≤ typeof(result).也就是说,如果方法被声明为:

Informally speaking, this statement is evaluated by assigning the value of a to the method's first parameter, then executing the body of the method, and then assigning the methods return value to result. Like the plain assignment in the last example, the "right hand side" must be a subtype of the "left hand side", i.e. this statement can only be valid if typeof(a) ≤ typeof(parameter(method)) and returntype(method) ≤ typeof(result). That is, if method is declared by:

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

以下表达式都不会编译:

none of the following expressions will compile:

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>());

会.

子类型很重要的另一个例子是压倒一切.考虑:

Another example where subtyping matters is overriding. Consider:

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

哪里

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

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

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

Informally, the runtime will rewrite this to:

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))> 必须至少是协变的.

For the marked line to compile, the method parameter of the overriding method must be a supertype of the method parameter of the overridden method, and the return type a subtype of the overridden method's one. Formally speaking, f(A) = parametertype(method asdeclaredin(A)) must at least be contravariant, and if f(A) = returntype(method asdeclaredin(A)) must at least be covariant.

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

Note the "at least" above. Those are minimum requirements any reasonable statically type safe object oriented programming language will enforce, but a programming language may elect to be more strict. In the case of Java 1.4, parameter types and method return types must be identical (except for type erasure) when overriding methods, i.e. parametertype(method asdeclaredin(A)) = parametertype(method asdeclaredin(B)) when overriding. Since Java 1.5, covariant return types are permitted when overriding, i.e. the following will compile in Java 1.5, but not in Java 1.4:

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

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

我希望我涵盖了所有内容 - 或者更确切地说,只是触及了表面.我仍然希望它有助于理解抽象但重要的类型变化概念.

I hope I covered everything - or rather, scratched the surface. Still I hope it will help to understand the abstract, but important concept of type variance.

这篇关于用简单的英语解释协变、不变和逆变?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆