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

时间:2016-02-27 11:25:32

标签: java generics

又一个新手,试图理解Java Generics。 我发现,我观察了所有主题,但我仍然有很多问题。 能否请您解释以下事项:

  1. <? extends SomeClass>表示?是“任何类型”,extends SomeClass表示此任何类型只能是SomeClass的子类。 好的,我写了两个小学课程:
  2. 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说:

      

    “方法在类型中添加(捕获#3-of?extends Person)   列表不适用于   争论(学生)“

    当我们声明List,按Student进行参数化,<? extends Person>完全扩展类Student时,类Person如何不适用?

    然而,以下代码:

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

    编译并运行良好(list.get(0),传递给println方法,向我显示toString调用的正确结果。 据我所知,List<? super Person>意味着,我可以将任何类型传递给此列表,这是我们Person类的超类型(在我们的例子中,它只是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>。这样做,我们的意思是,我们可以将子类存储在其超类的集合中。也许,我不了解一些基本的想法,但在这个阶段我没有看到将子类添加到其超类集合和将对象“clarissa”的引用分配给变量,类型Person之间的任何区别。当我们想要使用我们的超类变量来处理其中一个方法时,我们可以减少可调用的方法。那么,为什么List<? extends SomeClass>不能以相同的方式工作,List<? super SomeClass>反过来呢?

    1. 我不明白<T>(或<E>或来自JLS适当部分的任何其他来信)和<?>之间的根本区别。 <T><?>都是类型,为什么我们有两个“关键字”(这个符号不是关键字,我只是用这个词强调两个符号的重要含义在Java语言中出于同样的目的?

2 个答案:

答案 0 :(得分:23)

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

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

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

使用有界通配符与“&#39;标准”的对比。泛型类型参数是差异的值开始变得明显的地方。我们假设我有3个课程PersonStudentTeacher,其中PersonStudentTeacher扩展的基础。在API中,您可以编写一个方法,该方法采用Person的集合,并对集合中的每个项目执行某些操作。没关系,但你真的只关心这个集合是某种与Person界面兼容的类型 - 它应该同样适用于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> e.g。

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

答案 1 :(得分:3)

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

您可以执行上述操作的原因,假设有一个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
}

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