Java 用通俗易懂的英语解释协变、不变和逆变?

Java 用通俗易懂的英语解释协变、不变和逆变?,java,covariance,contravariance,Java,Covariance,Contravariance,今天,我读了一些关于Java中协方差、逆变换(和不变性)的文章。我读了英文和德文维基百科的文章,以及IBM的一些其他博客文章和文章 但我还是有点困惑这些到底是关于什么的?有人说它是关于类型和子类型之间的关系,有人说它是关于类型转换,有人说它是用来决定方法是被重写还是重载的 所以我想找一个简单的英语解释,向初学者说明协方差和逆变(以及不变性)是什么。这是一个简单的例子 有人说它是关于类型和子类型之间的关系,有人说它是关于类型转换,还有人说它是用来决定方法是被覆盖还是重载的 所有这些 本质上,这些术

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

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

所以我想找一个简单的英语解释,向初学者说明协方差和逆变(以及不变性)是什么。这是一个简单的例子

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

所有这些

本质上,这些术语描述了类型转换如何影响子类型关系。也就是说,如果
A
B
是类型,
f
是类型转换,并且≤ 子类型关系(即
A≤ B
意味着
A
B
的一个子类型,我们有

  • f
    是协变的,如果
    A≤ B
    表示
    f(A)≤ f(B)
  • 如果
    A,则
    f
    是逆变的≤ B
    表示
    f(B)≤ f(A)
  • f
    是不变的,如果上述两个都不成立
让我们考虑一个例子。设
f(A)=List
其中
List

class List<T> { ... } 
仅当
typeof(y)时才编译≤ 类型(x)
。也就是说,我们刚刚了解到

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

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

result = method(a);
非正式地说,通过将
a
的值赋给方法的第一个参数,然后执行方法体,然后将方法返回值赋给
结果来评估此语句。与上一个示例中的普通赋值一样,“右手边”必须是“左手边”的子类型,即此语句只有在
typeof(a)时才有效≤ 类型(参数(方法))
返回类型(方法)≤ 类型(结果)
。也就是说,如果方法声明为:

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

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

class Super {
    Number method(Number n) {
        if (this instanceof Sub) {
            return ((Sub) this).method(n);  // *
        } else {
            ... 
        }
    }
}
对于要编译的标记行,重写方法的方法参数必须是重写方法的方法参数的超类型,返回类型必须是重写方法的子类型。从形式上讲,
f(A)=参数类型(方法asdeclaredin(A))
必须至少是逆变的,如果
f(A)=返回类型(方法asdeclaredin(A))
必须至少是协变的

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

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

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

我希望我涵盖了一切——或者更确切地说,触及了表面。尽管如此,我还是希望它将有助于理解抽象但重要的类型差异概念。

学习java类型系统,然后学习类:

任何类型T的对象都可以用子类型T的对象替换

类型差异-类方法具有以下后果

class A {
    public S f(U u) { ... }
}

class B extends A {
    @Override
    public T f(V v) { ... }
}

B b = new B();
t = b.f(v);
A a = ...; // Might have type B
s = a.f(u); // and then do V v = u;
可以看出:

  • T必须是子类型(协变,因为B是A的子类型)
  • V必须是U的超型(逆变型,作为逆变继承方向)
现在,co-和contra-与B是A的亚型有关。以下更强的类型可能会介绍更具体的知识。在子类型中

协方差(在Java中可用)很有用,可以说返回子类型中更具体的结果;尤其是当A=T和B=S时。
Contravariance表示您准备处理一个更一般的参数。

方差是关于具有不同泛型参数的类之间的关系。他们之间的关系是我们可以铸造他们的原因

协方差和反方差是非常合乎逻辑的事情。语言类型系统迫使我们支持现实生活中的逻辑。通过例子很容易理解

协方差 例如,你想买一朵花,而你所在的城市有两家花店:玫瑰店和雏菊店

如果你问某人“花店在哪里?”有人告诉你玫瑰店在哪里,可以吗?是的,因为玫瑰是一朵花,如果你想买一朵花,你可以买一朵玫瑰。如果有人回复你雏菊店的地址,同样适用。 这是协方差的一个例子:如果
A
生成通用值(函数返回结果),则允许将
A
转换为
A
,其中
C
B
的子类。协方差与生产者有关

类型:

class Flower {  }
class Rose extends Flower { }
class Daisy extends Flower { }

interface FlowerShop<T extends Flower> {
    T getFlower();
}

class RoseShop implements FlowerShop<Rose> {
    @Override
    public Rose getFlower() {
        return new Rose();
    }
}

class DaisyShop implements FlowerShop<Daisy> {
    @Override
    public Daisy getFlower() {
        return new Daisy();
    }
}
interface PrettyGirl<TFavouriteFlower extends Flower> {
    void takeGift(TFavouriteFlower flower);
}

class AnyFlowerLover implements PrettyGirl<Flower> {
    @Override
    public void takeGift(Flower flower) {
        System.out.println("I like all flowers!");
    }

}
你把喜欢任何花的女朋友看作是喜欢玫瑰的人,送她一朵玫瑰:

PrettyGirl<? super Rose> girlfriend = new AnyFlowerLover();
girlfriend.takeGift(new Rose());

prettygirl请参考这篇文章,它可能会对您有所帮助:也许最好是一个程序员的堆栈交换类型问题。如果你在那里发表文章,考虑一下你所了解的,以及你特别困惑的,因为现在你要求某人为你重新编写一个完整的教程。此外,因为在重写时允许java 1.5逆变参数类型。我想你错过了,是吗?我只是在eclipse中尝试过,编译器认为我的意思是
class Super {
    Number method(Number n) {
        if (this instanceof Sub) {
            return ((Sub) this).method(n);  // *
        } else {
            ... 
        }
    }
}
class Collection {
    Iterator iterator() { ... }
}

class List extends Collection {
    @Override 
    ListIterator iterator() { ... }
}
class A {
    public S f(U u) { ... }
}

class B extends A {
    @Override
    public T f(V v) { ... }
}

B b = new B();
t = b.f(v);
A a = ...; // Might have type B
s = a.f(u); // and then do V v = u;
class Flower {  }
class Rose extends Flower { }
class Daisy extends Flower { }

interface FlowerShop<T extends Flower> {
    T getFlower();
}

class RoseShop implements FlowerShop<Rose> {
    @Override
    public Rose getFlower() {
        return new Rose();
    }
}

class DaisyShop implements FlowerShop<Daisy> {
    @Override
    public Daisy getFlower() {
        return new Daisy();
    }
}
static FlowerShop<? extends Flower> tellMeShopAddress() {
    return new RoseShop();
}
interface PrettyGirl<TFavouriteFlower extends Flower> {
    void takeGift(TFavouriteFlower flower);
}

class AnyFlowerLover implements PrettyGirl<Flower> {
    @Override
    public void takeGift(Flower flower) {
        System.out.println("I like all flowers!");
    }

}
PrettyGirl<? super Rose> girlfriend = new AnyFlowerLover();
girlfriend.takeGift(new Rose());