协方差,不变性和逆变性用简单的英语解释?

今天,我读了一些关于Java中的协变,逆变(和变化)的文章。 我阅读了英文和德文维基百科文章以及IBM的其他一些博文和文章。

但是我对这些究竟是关于什么还是有点困惑? 有人说这是关于types和子types之间的关系,有人说这是关于types转换,有人说它是用来决定一个方法是否被覆盖或重载。

所以我在简单的英语中寻找一个简单的解释,这表明一个初学者是什么协变和逆变(和不变性)。 加点为一个简单的例子。

有人说这是关于types和子types之间的关系,另一种说是关于types转换,另一些则说是用来决定一个方法是否被覆盖或重载。

以上所有。

从本质上讲,这些术语描述了子types关系如何受到types转换的影响。 也就是说,如果AB是types, f是types转换,并且≤子types关系(即A ≤ B意味着AB的子types),我们有

  • 如果A ≤ B意味着f(A) ≤ f(B) f是协变的,
  • 如果A≤B意味着f(B) ≤ f(A) f是逆变的,
  • 如果两者都不成立, f是不变的

我们来看一个例子。 设f(A) = List<A> ,其中List是由…声明的

 class List<T> { ... } 

是协变的,逆变的还是不变的? 协变将意味着List<String>List<Object>一个子types,与List<Object> List<String>List<String>的子types是互逆的,并且既不是另一个的子types,即List<String>List<Object>是不可转换的types。 在Java中,后者是真实的,我们说(有点非正式) generics是不变的。

另一个例子。 令f(A)= A[] 。 是协变的,逆变的还是不变的? 也就是说,String []是Object []的子types,Object []是String []的子types,还是既不是另一个的子types? (答:在Java中,数组是协变的)

这还是比较抽象的。 为了更具体一些,让我们来看看Java中的哪些操作是以子types关系来定义的。 最简单的例子是分配。 该声明

 x = y; 

只有在typeof(y)≤typeof(x)时才会编译。 也就是说,我们刚才了解到这一点

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

不会在Java中编译,但是

 Object[] objects = new String[1]; 

将。

子types关系很重要的另一个例子是方法调用expression式:

 result = method(a); 

非正式的说,这个语句通过赋值给方法的第一个参数,然后执行方法的主体,然后赋值方法返回结果来评估。 就像最后一个例子中的简单赋值一样,“右手边”必须是“左手边”的子types,即这个语句只有在typeof(a)≤typeof(参数(method))和returntype方法)≤typeof(结果)。 也就是说,如果方法被声明为:

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

下面的expression式都不会被编译:

 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 { ... } } } 

对于要编译的标记行,重写方法的方法参数必须是重写方法的方法参数的超types,返回types是重写方法的子types。 从forms上讲,f(A)=参数types(方法如declaredin(A))至less必须是逆变的,如果f(A)=返回types(方法如声明的(A))至less是协变的。

注意上面的“至less”。 这些是最低要求,任何合理的静态types安全的面向对象的编程语言将执行,但编程语言可能会select更严格。 在Java 1.4的情况下,当覆盖方法时,参数types和方法返回types必须是相同的(除了types擦除),即参数types(method asdeclaredin(A))= parametertype(methoddeclaredin(B))。 从Java 1.5开始,重写时允许使用协变返回types,即下面的代码将在Java 1.5中编译,而不是在Java 1.4中编译:

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

我希望我能覆盖所有的东西,或者说,抓住表面。 不过,我希望这将有助于理解抽象的,但是types方差的重要概念。

取javatypes的系统,然后类:

任何一个typesT的任何对象都可以用T的子types的对象代替。

types方差 – 类方法有以下后果

 class A { public S f(U u) { ... } } class B extends A { @Override public T f(V v) { ... } } B b = new B(); t = bf(v); A a = ...; // Might have type B s = af(u); // and then do V v = u; 

可以看出,

  • T必须是S型( 协变的,因为B是A的亚型 )。
  • V必须是U的超types( 逆变 ,如逆向inheritance方向)。

现在把B作为A的子types是合作的和相反的。以下更强的types可以引入更具体的知识。 在子types中。

协方差(可用Java)很有用,可以说在子types中返回更具体的结果。 特别是当A = T和B = S时看到。 反变换说你准备处理一个更一般的论点。