8

谁能解释一下?

我有这几节课:

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

这是行动:

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

此示例编译没有错误,输出为:

woof
woof
meow

但是我怎样才能添加猫狗列表?猫是怎么变成狗的?

我使用:java版本“1.6.0_24”。OpenJDK 运行时环境 (IcedTea6 1.11.1) (6b24-1.11.1-4ubuntu3)

4

6 回答 6

7

好的,这是泛型的处理(任何使用强制转换的hackery在运行时都可能不安全,因为泛型通过擦除工作):

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

List<Animal> l = new ArrayList<Animal>();

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

l.add(new Cat());
l.add(new Dog());

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

Animal a = l.get(0);
Cat c = l.get(0); //disallowed
Dog d = l.get(1); //disallowed

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

List<? extends Animal> l = new ArrayList<Animal>();
List<? extends Animal> l = new ArrayList<Cat>();
List<? extends Animal> l = new ArrayList<Dog>();

但您无法向此列表添加新项目

l.add(new Cat()); // disallowed
l.add(new Dog()); // disallowed

在您的情况下,您有 aList<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

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

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>();

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

l1.add(new Cat());
l1.add(new Dog());
l1.add(new Object()); //disallowed

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

Object o = l1.get(0);
Animal a = l1.get(0); //disallowed
Cat c = l2.get(0); //disallowed
Dog d = l3.get(0); //disallowed
于 2012-05-24T20:13:18.703 回答
2

不要期望泛型执行运行时类型检查。在编译期间,Java 执行所有类型推断,实例化所有类型,......然后从代码中删除所有泛型类型的痕迹。在运行时,类型是 List,而不是 List<T> 或 List<Dog>。

主要问题是,为什么它允许您强制转换new Cat()为 type T extends Animal,并且只对未经检查的转换发出警告,这是有效的。类型系统的某些不健全的特性使得将这种可疑的类型合法化成为必要。

如果您希望编译器阻止向列表中添加任何内容,则应使用通配符:

public void doAction(List< ? extends Animal > animals) {
    animals.add(new Cat()); // disallowed by compiler
    animals.add((Animal)new Cat()); // disallowed by compiler

    for (Animal animal: animals) {
        if (animal instanceof Cat) {
            ((Cat)animal).meow();
        }
        if (animal instanceof Dog) {
            ((Dog)animal).woof();
        }
    }
}

PS 循环体中可疑的向下转换是一个完美的例子,说明 Java 对于不相交的总和(变体)类型是多么蹩脚。

于 2012-05-24T19:26:50.937 回答
1

这与类型擦除有关。该类型在运行时不保留。实际上,List 在运行时变成了类型对象的列表。这就是为什么您会收到编译器警告或应该在 animals.add((T) new Cat()); 上的原因。在编译时,Cat 确实扩展了 T 类型的 animal。但是,它不能强制此时 Dog 在列表中,因此会发出编译器警告。

于 2012-05-24T19:21:15.420 回答
0

泛型仅适用于编译时安全。在您的情况下,编译器如何知道会发生不好的事情?它假定您的类型定义,并以此为基础。做更多的工作意味着编译器需要更精细的工作,但是有扩展的静态检查器和其他工具可以捕获这个预运行时。

于 2012-05-24T19:16:33.363 回答
0

只要说有这样一个T演员可以成功就够了。至于编译后的代码,和你写的完全一样animals.add((Animal)new Cat());

于 2012-05-24T19:21:57.473 回答
0

您的函数doActionT扩展类的类型参数化Animal。所以它可以是类型的对象,也可以DogCat. 您还应该注意形式参数有效参数
之间的区别。形式参数是您在定义方法时使用的参数,在您的情况下是(将其用作快捷方式)。有效参数是您在调用方法时“赋予”方法的参数,此处为。. 让我们来 看看。Animals 是一个列表,其中元素的类型是or ,所以没有错误,因为这是形式参数的类型。
List <T>
List<Dog>

animals.add((T) new Cat())doAction()TDogCat
所以这就是原因。这是使用参数化类的好处之一。

于 2012-05-24T19:33:00.220 回答