<? 扩展类> 和 <? Java 中的 super Class> - 为什么它以这种方式工作?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/35669112/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 00:26:26  来源:igfitidea点击:

<? extends Class> and <? super Class> in Java - why it works this way?

javagenerics

提问by Victor Zhdanov

Yet another novice, trying to comprehend Java Generics. I've observed all topics, I found, but I still have huge questions. Could you please explain me the following things:

另一个新手,试图理解 Java 泛型。我已经观察了所有主题,我发现,但我仍然有很多问题。你能不能解释一下以下几点:

  1. <? extends SomeClass>means, that ?is "any type", and extends SomeClassmeans, that this any type can be only a subclass of SomeClass. OK, I write two elementary classes:
  1. <? extends SomeClass>意味着,即?“任何类型”,并extends SomeClass意味着,此任何类型只能是SomeClass. 好的,我写了两个基本类:
abstract class Person {
    private String name;
    public Person(String name) {
        this.name = name;
    }
}

class Student extends Person {
    public Student(String name) {
        super(name);
    }
}

Class Studentwill be ?in our example. ? extends Person, to be precise. Then I'm trying to add new student to ArrayList, that, as I understand from written above, applies all classes, that are subclasses of Person:

Student?在我们的示例中。? extends Person,准确地说。然后我试图将新学生添加到 ArrayList,正如我从上面所理解的那样,应用所有类,即 Person 的子类:

Student clarissa = new Student("Clarissa Starling");
List<? extends Person> list = new ArrayList<>();
list.add(clarissa); //Does not compile

Eclipse says:

Eclipse 说:

"The method add(capture#3-of ? extends Person) in the type List is not applicable for the arguments (Student)"

“类型列表中的方法 add(capture#3-of ? extends Person) 不适用于参数 (Student)”

How can class Studentbe not applicable, when we declared List, paramethrized by <? extends Person>, and Studentexactly extends class Person?

Student当我们声明 List、参数化为<? extends Person>并且Student完全扩展 class时,class 怎么可能不适用Person

Nevertheless, the following code:

尽管如此,以下代码:

List<? super Person> list = new ArrayList<>();
list.add(clarissa); 

compiles and works well (list.get(0), passed to println method, shows me the correct result of toStringinvocation). As I understand, List<? super Person>means, that I can pass to this list any type, that is super type for our Personclass (in our case it is Objectclass only). But we see, that, contrary to logic, we can easy add subclass Student to our List<? super Person>!

编译并运行良好(list.get(0),传递给 println 方法,向我显示toString调用的正确结果)。据我了解,这List<? super Person>意味着我可以将任何类型传递给这个列表,即我们Person类的超类型(在我们的例子中它只是Object类)。但是我们看到,与逻辑相反,我们可以轻松地将子类 Student 添加到我们的List<? super Person>!

OK, put aside our emotions, and let's see, what can happen with Clarissa Starling in our collection. Let's take our class Student, and add a couple of methods to it:

好吧,抛开我们的情绪,让我们看看在我们的收藏中 Clarissa Starling 会发生什么。让我们拿我们的 class Student,并添加几个方法到它:

class Student extends Person {
    private int grant;
    public Student(String name) {
        super(name);
    }

    public void setGrant(int grant) {
        this.grant = grant;
    }

    public int getGrant() {
        return this.grant;
    }

}

Then we pass an object, instantiated from this renewed class (our object "clarissa", for example), to List<? extends Person>. Doing this, we mean, that we can store subclass in the collection of its superclasses. Maybe, I don't understand some fundamenthal ideas, but at this stage I don't see any difference between adding subclass to the collection of its superclasses and the assigning of the reference to object "clarissa" to variable, typed Person. We have the same reducing of invokable methods, when we want to treat one of them, using our superclass variable. So, why List<? extends SomeClass>does not work the same way, wherein List<? super SomeClass>works conversely?

然后我们将一个从这个更新的类(例如我们的对象“clarissa”)实例化的对象传递给List<? extends Person>. 这样做,我们的意思是,我们可以将子类存储在其超类的集合中。也许,我不明白一些基本的想法,但在这个阶段,我没有看到将子类添加到其超类的集合与将对象“clarissa”的引用分配给变量类型的 Person 之间有任何区别。当我们想使用超类变量处理其中一个方法时,我们对可调用方法进行了相同的减少。那么,为什么List<? extends SomeClass>不以相同的方式工作,而其中的List<? super SomeClass>工作方式相反呢?

  1. I don't understand the fundamenthal difference between <T>(or <E>, or any other letter from appropriate part of JLS) and <?>. Both <T>and <?>are typeholders, so why we have two "keywords" (this symbols are NOT keywords, I just used this word for emphasizing the heavy meaning of both symbols in Java language) for the same purpose?
  1. 我不明白<T>(或<E>,或来自 JLS 适当部分的任何其他字母)和<?>. 这两个<T><?>typeholders,那么为什么我们有两个“关键词”(这个符号是不是关键字,我只是用这个词强调Java语言中的两个符号的重意思)为了同样的目的?

回答by sisyphus

The way I look at it is this - the placeholder Tstands in for a definite type and in places where we need to know the actual type we need to be able to work it out. In contrast the wildcard ?means any type and I will never need to know what that type is. You can use the extendsand superbounds to limit that wildcard in some way but there's no way to get the actual type.

我的看法是这样的 - 占位符T代表一个确定的类型,在我们需要知道实际类型的地方,我们需要能够解决它。相比之下,通配符?表示任何类型,我永远不需要知道该类型是什么。您可以使用extendssuper边界以某种方式限制该通配符,但无法获得实际类型。

So, if I have a List<? extends MySuper>then all I know about it is that every object in it implements the MySuperinterface, and all the objects in that list are of the same type. I don't know what that type is, only that it's some subtype of MySuper. That means I can get objects out of that list so long as I only need to use the MySuperinterface. What I can't do is to put objects into the list because I don't know what the type is - the compiler won't allow it because even if I happen to have an object of the right type, it can't be sure at compile time. So, the collection is, in a sense a read-only collection.

因此,如果我有一个,List<? extends MySuper>那么我所知道的就是它中的每个对象都实现了该MySuper接口,并且该列表中的所有对象都属于同一类型。我不知道那种类型是什么,只知道它是MySuper. 这意味着只要我只需要使用MySuper接口,我就可以从该列表中获取对象。我不能做的是将对象放入列表中,因为我不知道类型是什么 - 编译器不会允许它,因为即使我碰巧有一个正确类型的对象,它也不能在编译时确定。因此,从某种意义上说,该集合是只读集合。

The logic works the other way when you have List<? super MySuper>. Here we're saying the collection is of a definite type which is a supertype of MySuper. This means that you can always add a MySuperobject to it. What you can't do, because you don't know the actual type, is retrieve objects from it. So you've now got a kind of write-only collection.

当您有List<? super MySuper>. 这里我们说集合是一个确定的类型,它是 的超类型MySuper。这意味着您始终可以向其添加MySuper对象。你不能做的,因为你不知道实际的类型,是从中检索对象。所以你现在有一种只写集合。

Where you use a bounded wildcard versus the 'standard' generic type parameter is where the value of the differences start to become apparent. Let's say I have 3 classes Person, Studentand Teacher, with Personbeing the base that Studentand Teacherextend. In an API you may write a method that takes a collection of Personand does something to every item in the collection. That's fine, but you really only care that the collection is of some type that is compatible with the Personinterface - it should work with List<Student>and List<Teacher>equally well. If you define the method like this

使用有界通配符与“标准”泛型类型参数的地方是差异的价值开始变得明显的地方。比方说,我有3个班PersonStudentTeacherPerson作为基础,StudentTeacher扩展。在 API 中,您可以编写一个方法来获取集合中的Person每个项目并对其执行某些操作。这很好,但你真的只关心集合是与Person接口兼容的某种类型- 它应该可以工作List<Student>并且List<Teacher>同样好。如果你这样定义方法

public void myMethod(List<Person> people) {
    for (Person p: people) {
        p.doThing();
    }
}

then it can't take List<Student>or List<Teacher>. So, instead, you would define it to take List<? extends Person>...

那么它不能采取List<Student>List<Teacher>。因此,相反,您可以将其定义为List<? extends Person>……

public void myMethod(List<? extends Person> people){
    for (Person p: people) {
        p.doThing();
    }
}

You can do that because myMethodnever needs to add to the list. And now you find that List<Student>and List<Teacher>can both be passed into the method.

您可以这样做,因为myMethod永远不需要添加到列表中。现在您发现List<Student>List<Teacher>都可以传递到方法中。

Now, let's say that you've got another method which wants to add Students to a list. If the method parameter takes a List<Student>then it can't take a List<People>even though that should be fine. So, you implement it as taking a List<? super Student>e.g.

现在,假设您有另一种方法想要将学生添加到列表中。如果方法参数采用 aList<Student>那么它不能采用 aList<People>即使这应该没问题。所以,你把它实现为一个List<? super Student>eg

public void listPopulatingMethod(List<? extends Student> source, List<? super Student> sink) {
    for (Student s: source) {
        sink.add(s);
    }
}

This is the heart of PECS, which you can read about in much greater detail elsewhere... What is PECS (Producer Extends Consumer Super)?http://www.javacodegeeks.com/2011/04/java-generics-quick-tutorial.html

这是 PECS 的核心,您可以在别处更详细地了解它... 什么是 PECS(生产者扩展消费者超级)?http://www.javacodegeeks.com/2011/04/java-generics-quick-tutorial.html

回答by Li Jing

List<? super Person> list = new ArrayList<>();
list.add(clarissa); // clarissa is an instance of Student class

The reason why you can do things above, Suppose there is a Person class, Student class extends Person. You can think List means that in this List all elements is of class Person or superclass of Person, so when you add Person class instance or subclass of Person class, the implicit casting will happen. e.g. Person person = new Student();

之所以可以做上面的事情,假设有一个Person类,Student类扩展Person。你可以认为List意味着这个List中的所有元素都属于Person类或Person的超类,所以当你添加Person类实例或Person类的子类时,就会发生隐式转换。例如 Person person = new Student();

public static void main(String[] args) {
        ArrayList<? super Person> people = new ArrayList<>();
        people.add(new Person());
        people.add(new Object()); // this will not compile
}

But if you add Object class instance into the list, explicit casting or down casting is required and the compiler does not know whether say (Person) Object can be successful.

但是如果将 Object 类实例添加到列表中,则需要显式转换或向下转换,并且编译器不知道 say (Person) Object 是否可以成功。