&lt ;?扩展Class>和&lt ;?超级类>在Java中-为什么这样工作? [英] <? extends Class> and <? super Class> in Java - why it works this way?

查看:46
本文介绍了&lt ;?扩展Class>和&lt ;?超级类>在Java中-为什么这样工作?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

另一个新手,试图理解Java泛型.我发现我已经观察了所有主题,但是仍然有很多问题.您能给我解释一下以下内容吗?

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:

  1. < ;?extend SomeClass> 表示?是任何类型",而 extends SomeClass 表示该任何类型只能是 SomeClass的子类.好吧,我写了两个基本类:
  1. <? extends SomeClass> means, that ? is "any type", and extends SomeClass means, that this any type can be only a subclass of SomeClass. OK, I write two elementary classes:

abstract class Person {
    private String name;
    public Person(String name) {
        this.name = name;
    }
}

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

在我们的示例中,

学生将是?.?准确地说,扩展了Person .然后,我尝试将新学生添加到ArrayList中,据我从上面的描述中了解,该类适用于Person的所有子类:

Class Student will 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 clarissa = new Student("Clarissa Starling");
List<? extends Person> list = new ArrayList<>();
list.add(clarissa); //Does not compile

Eclipse说:

类型中的方法add(capture#3-of扩展Person)清单不适用于论点(学生)"

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

当我们声明List时, Student 类怎么不适用?扩展Person> ,而 Student 恰好扩展了 Person 类?

How can class Student be not applicable, when we declared List, paramethrized by <? extends Person>, and Student exactly extends class Person?

尽管如此,以下代码:

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

编译并运行良好(传递给println方法的 list.get(0)向我展示了 toString 调用的正确结果).据我了解, List< ;?super Person> 的意思是,我可以将任何类型(对于我们的 Person 类是超级类型)传递给此列表(在我们的情况下,它仅是 Object 类).但是我们看到,与逻辑相反,我们可以轻松地将子类Student添加到我们的 List< ;?中?超级人物>

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

好吧,撇开我们的情绪,让我们看看,克拉丽莎·史达琳(Clarissa Starling)在我们的收藏中会发生什么.让我们来学习我们的类 Student ,并为其添加一些方法:

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:

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;
    }

}

然后,我们将一个对象(从这个更新的类(例如,我们的对象"clarissa")实例化)传递给 List< ;?扩展Person> .这样做意味着我们可以将子类存储在其超类的集合中.也许,我不了解一些基本的想法,但是在这个阶段,我看不出将子类添加到其超类的集合中,以及将对对象"clarissa"的引用分配给变量类型的Person之间没有任何区别.当我们想使用我们的超类变量来对待其中一种方法时,可以减少可调用方法的数量.那么,为什么 List< ;?扩展SomeClass> 的工作方式不同,其中 List< ;?超级SomeClass> 是否可以正常工作?

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?

  1. 我不理解< T> (或< E> 或JLS适当部分的任何其他字母)与<?> .< T> <?> 都是 typeholders ,所以为什么我们有两个关键字"(这些符号不是关键字,我只是用这个词来强调Java语言中两个符号的含义很重吗?
  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?

推荐答案

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

The way I look at it is this - the placeholder T stands 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 extends and super bounds to limit that wildcard in some way but there's no way to get the actual type.

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

So, if I have a List<? extends MySuper> then all I know about it is that every object in it implements the MySuper interface, 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 MySuper interface. 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< ;?时,逻辑将以另一种方式起作用.超级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 MySuper object 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.

在使用有界通配符与标准"泛型类型参数的地方,差异的值开始变得明显.假设我有3个班级 Person Student Teacher ,其中 Person Student的基础 Teacher 扩展.在API中,您可以编写一个方法,该方法采用 Person 的集合,并对集合中的每个项目都执行某项操作.很好,但是您实际上只关心集合是否与 Person 接口兼容的某种类型-它应该与 List< Student> List< Teacher> 一样好.如果您定义这样的方法

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, Student and Teacher, with Person being the base that Student and Teacher extend. In an API you may write a method that takes a collection of Person and 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 Person interface - it should work with List<Student> and List<Teacher> equally well. If you define the method like this

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

那么它就不会接受 List< Student> List< Teacher> .因此,您可以将其定义为采用 List<?.扩展Person> ...

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

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

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

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

现在,假设您有另一种方法希望将学生"添加到列表中.如果method参数采用 List< Student> ,则即使采用这种方法也不能采用 List< People> .因此,您将其实现为采用 List< ;?超级学生> 例如

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.

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

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

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

这篇关于&lt ;?扩展Class&gt;和&lt ;?超级类&gt;在Java中-为什么这样工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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