Java Generics:在集合中添加错误的类型

Kyr*_*iev 7 java generics bounded-wildcard

谁能解释一下这个?

我有几个班:

abstract class Animal {
    public void eat() {
        System.out.println("Animal is eating");
    }
}

class Dog extends Animal {
    public void woof() {
        System.out.println("woof");
    }
}

class Cat extends Animal {
    public void meow() {
        System.out.println("meow");
    }
}
Run Code Online (Sandbox Code Playgroud)

这就是行动:

import java.util.ArrayList;
import java.util.List;

public class TestClass {

    public static void main(String[] args) {
        new TestClass().go();
    }

    public void go() {
        List<Dog> animals = new ArrayList<Dog>();
        animals.add(new Dog());
        animals.add(new Dog());
        doAction(animals);
    }

    public <T extends Animal> void doAction(List<T> animals) {

        animals.add((T) new Cat()); // why is it possible? 
                                    // Variable **animals** is List<Dog>, 
                                    // it is wrong, that I can add a Cat!

        for (Animal animal: animals) {
            if (animal instanceof Cat) {
                ((Cat)animal).meow();
            }
            if (animal instanceof Dog) {
                ((Dog)animal).woof();
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这个例子编译没有错误,输出是:

woof
woof
meow
Run Code Online (Sandbox Code Playgroud)

但是我如何添加Dog a Cat列表呢?那猫如何被送到狗?

我用的是:java版"1.6.0_24".OpenJDK运行时环境(IcedTea6 1.11.1)(6b24-1.11.1-4ubuntu3)

Rob*_*ert 6

好的,这是与泛型的交易(任何使用cast hackery的东西在运行时可能都不安全,因为泛型通过擦除工作):

您可以以相同的方式分配参数化的子类型,例如

List<Animal> l = new ArrayList<Animal>();
Run Code Online (Sandbox Code Playgroud)

并且您可以添加作为此参数的类型或其子类的项目,例如

l.add(new Cat());
l.add(new Dog());
Run Code Online (Sandbox Code Playgroud)

但你只能得到参数的类型:

Animal a = l.get(0);
Cat c = l.get(0); //disallowed
Dog d = l.get(1); //disallowed
Run Code Online (Sandbox Code Playgroud)

现在,您可以使用通配符设置参数类型的上限

List<? extends Animal> l = new ArrayList<Animal>();
List<? extends Animal> l = new ArrayList<Cat>();
List<? extends Animal> l = new ArrayList<Dog>();
Run Code Online (Sandbox Code Playgroud)

但是您无法在此列表中添加新项目

l.add(new Cat()); // disallowed
l.add(new Dog()); // disallowed
Run Code Online (Sandbox Code Playgroud)

在你的情况下,你有一个List<T>方法,add(T t)所以你可以添加,如果你强制转换T.但是T类型限制在上面,Animal因此您甚至不应该尝试添加到此列表中,但它被视为具体类型,这就是它允许强制转换的原因.然而,这可能会引发一场ClassCastException.

而且您只能检索上限类型的项目

Animal a = l.get(0);
Cat c = l.get(0); //disallowed
Dog d = l.get(1); //disallowed
Run Code Online (Sandbox Code Playgroud)

或者您可以设置下限参数类型

List<? super Animal> l1 = new ArrayList<Object>();
List<? super Animal> l1 = new ArrayList<Animal>();
List<? super Cat> l2 = new ArrayList<Animal>();
List<? super Cat> l2 = new ArrayList<Cat>();
List<? super Dog> l3 = new ArrayList<Animal>();
List<? super Dog> l3 = new ArrayList<Dog>();
Run Code Online (Sandbox Code Playgroud)

并且您可以添加作为下限类型的子类型的对象

l1.add(new Cat());
l1.add(new Dog());
l1.add(new Object()); //disallowed
Run Code Online (Sandbox Code Playgroud)

但是检索的所有对象都是Object类型

Object o = l1.get(0);
Animal a = l1.get(0); //disallowed
Cat c = l2.get(0); //disallowed
Dog d = l3.get(0); //disallowed
Run Code Online (Sandbox Code Playgroud)