简体   繁体   English

Java中的泛型类型-高级多态

[英]Generic types in Java - advanced polymorphism

I have 3 simple classes as follows: 我有3个简单的类,如下所示:

public class ElementA {}
public class ElementB extends ElementA {}
public class ElementC extends ElementB {}

Then if I want to create, for example, generic List which takes only subclasses of ElementA class I can declare it as: 然后,如果我想创建例如仅包含ElementA类的子类的通用List,则可以将其声明为:

List<? super ElementA> list = new ArrayList<>();

and then use it as follows: 然后按如下方式使用它:

list.add(new ElementA());
list.add(new ElementB());
list.add(new ElementC());

which is fine and can be compiled without errors. 很好,可以编译而不会出错。 But I became confused if I want to store anything but not ElementC or ElementB or ElementA. 但是,如果我想存储除ElementC或ElementB或ElementA以外的任何内容,我会感到困惑。 I declare such List as follows: 我声明这样的列表如下:

List<? extends ElementC> list = new ArrayList<>();

and I can't use it at all because it can store only null values. 而且我根本无法使用它,因为它只能存储空值。 Same thing happen when I declare List as (notice that I'm using class which is 'in the middle of family'): 当我将List声明为(注意,我正在使用位于“家庭中间”的类)时,会发生相同的事情:

List<? extends ElementB>

Why so? 为什么这样?

The problem is that the value of ? 问题在于? is not known at runtime. 在运行时未知。 You have to substitute a concrete class/interface in order to be able to do what you want. 您必须替换具体的类/接口才能执行所需的操作。

If you do this: 如果您这样做:

List<ElementA> list = new ArrayList<ElementA>();

you are fine since ElementB is an ElementA at the same time. 您很好,因为ElementB ElementA Same stands for ElementC . 同样代表ElementC

List<? extends ElementA> List<? extends ElementA> makes sense if you for example declare it in a class and in a subclass you can substitute something concrete as the type parameter. 如果您在类中声明它,并且在子类中可以将一些具体的东西替换为type参数,则List<? extends ElementA>有意义。 Clumsy example: 笨拙的例子:

public class SomeClass<T> {
    private List<? extends T> list;

    public void setList(List<? extends T> list) {
        this.list = list;
    }
}

public class SomeConcreteClass extends SomeClass<Integer> {

    public void doSomething() {
        List<Integer> list = new ArrayList<Integer>();
        setList(list);
    }
}

List<ElementA> accepts instances of ElementA , ElementB , and Element C . List<ElementA>接受ElementAElementBElement C实例。

List<ElementB> accepts instances of ElementB and Element C . List<ElementB>接受ElementBElement C实例。

List<ElementC> accepts instances of ElementC . List<ElementC>接受ElementC实例。

There is no reason for the wildcard in your examples. 您的示例中没有理由使用通配符。

List<? super ElementA> List<? super ElementA> means a List of some type which is ElementA or a superclass. List<? super ElementA>表示某种类型的List,它是ElementA或超类。

List<? extends ElementB> List<? extends ElementB> means a List of some type which is a subclass of ElementB . List<? extends ElementB>表示某种类型的List,它是ElementB的子类。 If you get an element it will be ElementB or a subclass, but it doesn't know what the class is, so it can't be sure the element you add is of the right type, since it is unknown (though it does know it to be a subclass of ElementB ). 如果得到一个元素,它将是ElementB或子类,但是它不知道该类是什么,因此不能确定添加的元素的类型正确,因为它是未知的(尽管它确实知道它是ElementB的子类)。

There are uses for wildcard, but your example is not one of them. 通配符有一些用途,但您的示例不是其中之一。

You create a List like this 您创建一个这样的List

List<? extends ElementC> list = new ArrayList<>();

but let's say, because it's still valid that you got the List like this 但可以说,因为这样获得List仍然有效

List<? extends ElementC> list = getElementCSubclassList(); // declared as returning a `List<ElementCSubclass>`

Now the compiler cannot know that your list object contains ElementCSubclass objects, it can only be sure that it contains some type of ElementC . 现在,编译器无法知道您的list对象包含ElementCSubclass对象,因此只能确保它包含某种类型的ElementC As such, it can't let you use any methods that expect the actual generic type. 因此,它不能让您使用任何期望实际通用类型的方法。

Imagine 想像

public class ElementCSubclass1 extends ElementC {}
public class ElementCSubclass2 extends ElementC {}
...
List<? extends ElementC> list = getElementCSubclass1List(); // declared as returning a `List<ElementCSubclass1>`

list.add(new ElementCSubclass2()); // this would immediately have to fail

Compiler does this so that the previous situation never occurs. 编译器这样做是为了使以前的情况永远不会发生。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM