我对Java(和英语)还很陌生,所以请耐心等待。
试着写一些类似。。。
Container con = new Container<Book>();
con.insert(new Book());
con.insert(new Car());
并且没有得到任何类型的错误。但是像这样的台词。。。
Car c = con.remove(); // removes the last inserted element for simplicity
说"错误:不兼容的类型",所以我把它改成
Object carObj = (Car) con.remove();
它奏效了。我的问题是:当我说时
new Container<Book>();
我创建了一个只能容纳Book类型对象的容器,但由于指针(这是非通用的?),我可以突然将任何类型的对象放在我的容器中。这里发生了什么?指针只在容器中的任何内容中看到Object个性,但我不知道指针允许容器中的每个对象都具有Object个性,该容器主要创建为泛型(我的公式可能是错误的)。所以,当我有一个非泛型指针时,我创建的是泛型容器还是非泛型容器都无关紧要?它总是被认为是一个非通用容器(当我删除对象时,我必须强制转换对象)?
new Container<Book>().insert(new Car()); // compiler error as excepted
变得好奇,使问题变得更糟(也许)。
Container<Car> cars = new Container();
cars.insert(new Book()); // compiler error: required Car, found Book
现在指针只看到容器中的Car个性。但是,即使我将容器创建为非通用容器,它也不允许我放入书中。为什么?
new Container().insert(new Car()); // works fine
不得不说,它既迷人又令人恼火。。。
您正在对引用进行操作:引用的类型将在编译时使用。将Book
插入Container<Car>
显然是错误的,就像将Book
或Car
插入Container
没有错一样。
类似地,当引用只是<Container>
时,期望Container.remove
返回Car
是不正确的,因为没有理由期望返回的对象是Car
–它可能是CCD_ 10或鱼。
您的容器是一个原始容器,而不是通用容器。它被声明为Container
。它应该声明为Container<Book>
。
一旦完成,线路
con.insert(new Car());
将不再编译。
在Java中,对象的泛型类型只是编译时的事情。在运行时,由于擦除,它只是一个容器。因此,如果您不将容器声明为Container<Book>
,那么您将拥有一个原始容器,编译器将不会检查您存储在其中的对象类型。
为了更清楚(至少我希望如此),行
Container con = new Container<Book>();
相当于
Container con = (Container) (new Container<Book>());
它将对Container<Book>
的引用转换为对原始Container的引用,破坏了其类型安全性。
Java添加了泛型,因为已经有很多代码是在没有泛型的情况下编写的。泛型的设计者希望将List
等标准库容器更改为List<X>
,但已经有很多代码只使用普通的List
编写,如果他们要求所有用途都变成List<X>
用途,这些代码将不再编译。此外,他们希望人们使用自己的遗留库,例如,要求List
,并希望它只包含String
实例,以便能够传入List<String>
。
他们处理这一问题的方法是为每个泛型类型引入"原始类型"的概念,这基本上就是你不在泛型类型后面写尖括号得到的类型(例如List
而不是List<String>
)。原始类型与非原始类型具有不同的类型检查规则;例如,如果你有一个List
,那么add
和任何Object
都是合法的,但当你返回某个get
时,它会返回为你需要下变频的Object
,而List<String>
只允许你返回add
和String
,但作为回报,你不需要下变频get
的结果就可以返回String
。
不幸的是,由于语言设计者制定的向后兼容性规则,他们允许您编写类似的expession
List rawList = new ArrayList<String>();
rawList.add(new PeanutButterSandwich());
没有编译时错误。你甚至可以做更糟糕的事情,比如
List<String> stringList = new ArrayList<String>();
stringList.add("string");
List rawList = stringList;
List<PeanutButterSandwich> sandwichList = (List<PeanutButterSandwich>) rawList;
PeanutButterSandwich sandwich = sandwichList.get(0);
它进行编译(带有关于未检查强制转换的警告,这意味着您从原始类型强制转换为泛型类型,但编译器不知道它是合法的)。当然,这段代码在运行时肯定会引发异常,因为位置0中的元素是String
,而不是PeanutButterSandwich
。
重要的是要记住,原始类型只是为了向后兼容,不应该在新代码中使用它们。此外,如果您正在处理原始类型,在将它们转换为泛型类型时要非常小心,因为编译器无法阻止您做错误的事情。