java - <?扩展类> 和 <? Java 中的 super Class> - 为什么它会这样工作?

标签 java generics

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

  1. <? extends SomeClass>表示 ?是“任何类型”,extends SomeClass意味着,这 any 类型只能是 SomeClass 的子类. 好的,我写了两个初级类:
abstract class Person {
    private String name;
    public Person(String name) {
        this.name = name;
    }
}

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

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

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

Eclipse 说:

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

如何上课Student不适用,当我们声明 List 时,由 <? extends Person> 参数化, 和 Student完全扩展类 Person ?

不过,下面的代码:

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

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

好吧,抛开我们的情绪,让我们看看 Clarissa Starling 在我们的系列中会发生什么。上我们的课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;
    }

}

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

  1. 我不明白 <T> 之间的根本区别(或 <E> 或来自 JLS 适当部分的任何其他字母)和 <?> .两个<T><?>typeholders,那么为什么我们有两个“关键字”(这个符号不是关键字,我只是用这个词来强调 Java 语言中两个符号的沉重含义)?

最佳答案

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

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

当你有 List<? super MySuper> 时,逻辑会以另一种方式工作。 .这里我们说集合是一个确定的类型,它是 MySuper 的父类(super class)型。 .这意味着您可以随时添加 MySuper反对它。由于您不知道实际类型,您不能做的是从中检索对象。所以你现在有了一种只写集合。

在您使用有界通配符与“标准”泛型类型参数的地方,差异的值(value)开始变得明显。假设我有 3 节课 Person , StudentTeacher , 与 Person作为 Student 的基础和 Teacher延长。在 API 中,您可以编写一个方法来获取 Person 的集合。并对集合中的每个项目做一些事情。这很好,但您真的只关心该集合是与 Person 兼容的某种类型。接口(interface) - 它应该与 List<Student> 一起使用和 List<Teacher>同样好。如果你这样定义方法

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

那么它不能带List<Student>List<Teacher> .因此,您可以将其定义为 List<? extends Person> ...

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

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

现在,假设您有另一种方法想要将学生添加到列表中。如果方法参数采用 List<Student>那么它不能接受 List<People>即使那应该没问题。因此,您将其实现为采用 List<? super Student> 例如

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

这是 PECS 的核心,您可以在其他地方更详细地了解它... What is PECS (Producer Extends Consumer Super)? http://www.javacodegeeks.com/2011/04/java-generics-quick-tutorial.html

关于java - <?扩展类> 和 <? Java 中的 super Class> - 为什么它会这样工作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35669112/

相关文章:

c# - 泛型方法的可重用非泛型方法

java - 直接在 JBoss 服务器上配置 javax.faces.FACELETS_REFRESH_PERIOD

java - Eclipse 中的 Web 应用程序库为空 - 找不到 "jars"

java - JDBC 类型 4 驱动程序

java - 为什么泛型类型参数 get 方法会抛出 NullPointerException

JAVA 和泛型类型问题

java - 自定义 Spring Data Rest @ManyToMany 关系处理

java - 通过java在mysql中保存俄语

c# - 使用泛型接口(interface)的 WCF RESTful 服务

Java 8 map 签名 : public<U> Optional<U> map(Function<? super T, ? extends U> mapper)--为什么有两个U?