English 中文(简体)
Java 常规: 在收藏中添加错误类型
原标题:Java Generics: adding wrong type in collection

谁能解释这个?

我有几门课:

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

但是,我怎样才能在《狗猫》的列表中加进《狗猫》呢?《猫猫》是如何被投给《狗狗》的呢?

I use: java version "1.6.0_24". OpenJDK Runtime Environment (IcedTea6 1.11.1) (6b24-1.11.1-4ubuntu3)

最佳回答

Ok这里是通用名词的交易(任何使用刻画黑客的东西在运行时可能不安全,因为使用erasure 的通用名词有效):

您可以以同样的方式指定子类型参数, 例如 。

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

在您有 List <T> 所以它有方法 addadd(T) , 这样您可以投到 T 。 但是, T 键入了上面的界限 > Animal , 这样您甚至不应该试图添加到此列表中, 但是它被当作一个具体类型, 并且它允许投出。 但是, 这可能会丢出一个 ClassCastexpeption

并且只能检索上约束类型中的项目

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 o = l1.get(0);
Animal a = l1.get(0); //disallowed
Cat c = l2.get(0); //disallowed
Dog d = l3.get(0); //disallowed
问题回答

不要期望非专利类进行运行时间类型检查。 在编译过程中, Java 执行所有类型的推论, 即时解析所有类型,... 然后从代码中删除所有类型。 在运行时, 类型是列表, 不是列表 & lt; T & gt; 或 List< Dog & gt; 。

主要问题是为什么它允许您投下 < code> new Cat () 键入 < code> 延伸动物 , 仅警告未加节制的转换。 类型系统中的某些不健全的特性使得这种可疑的拼写合法化是必要的 。

如果您想要编译器防止列表中添加 anything , 您应该使用通配符 :

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

P. S. 环形体中可疑的下流者 是一个完美的例子 说明爪哇对于脱节和(变数)类型来说 是多么的跛脚

此选项与类型取消有关。 类型在运行时不保存。 确实, 列表在运行时成为类型对象列表。 这就是为什么您正在获取编译器警告或者应该在动物身上。 add (( T) 新 Cat () ); 编译时, Cat 确实扩展了属于 T 类的动物。 但是, 它无法强制在当时的列表中显示 Dog, 因此编译器警告 。

通用数据只用于编译时间安全 。 在您的情况中, 编译者如何知道坏事会发生? 它假定您的类型定义, 并从中得出结果。 做更多的工作意味着对编译者进行更细致的工作, 但是有扩展的静态复选器和其他工具可以赶上这个预运行前的时间 。

只需说有这样的 < code> T 即可成功。 至于已编译的代码, 它将与您写入 animals.add (( Animal) new Cat () ; 完全相同 。

Your function doAction is parametrized by a type T that extends the class Animal. So it can be either an object of type Dog or Cat.
You should also note the difference between formal parameter and effective parameter.
The formal parameter is the one you use when defining your method, which in your case is List <T> (using it as a shortcut).
The effective parameter is the one you "give" to your method when call it, here List<Dog>.
.
Lets take a look at animals.add((T) new Cat()) in doAction(). animals is a list which elements are of type T which is either Dog or Cat, so there is no mistake, since that s the type of the formal parameter.
So that s the reason. It is one of the benefits of using parametrize classes.





相关问题
Spring Properties File

Hi have this j2ee web application developed using spring framework. I have a problem with rendering mnessages in nihongo characters from the properties file. I tried converting the file to ascii using ...

Logging a global ID in multiple components

I have a system which contains multiple applications connected together using JMS and Spring Integration. Messages get sent along a chain of applications. [App A] -> [App B] -> [App C] We set a ...

Java Library Size

If I m given two Java Libraries in Jar format, 1 having no bells and whistles, and the other having lots of them that will mostly go unused.... my question is: How will the larger, mostly unused ...

How to get the Array Class for a given Class in Java?

I have a Class variable that holds a certain type and I need to get a variable that holds the corresponding array class. The best I could come up with is this: Class arrayOfFooClass = java.lang....

SQLite , Derby vs file system

I m working on a Java desktop application that reads and writes from/to different files. I think a better solution would be to replace the file system by a SQLite database. How hard is it to migrate ...

热门标签