既然问题中提到了我,我就插话。
基本上,如果你不向类外部公开这个数组变量,它不会引起任何问题(有点像,在拉斯维加斯发生的事情留在拉斯维加斯。
数组的实际运行时类型是 。因此,将其放入类型的变量中实际上是一个“谎言”,因为它不是(除非是)的子类型。但是,如果这个谎言留在类内,则此谎言是可以的,因为该谎言被擦除到类内部(的下限在这个问题中。如果 的下限是其他内容,请将此讨论中出现的所有内容替换为该边界。但是,如果这个谎言以某种方式暴露在外部(最简单的例子是将变量直接返回为类型,那么它将导致问题。Object[]
Bar[]
Object[]
Bar[]
Object
Bar
Bar
Object
Bar
Object
Bar
Object
bars
Bar[]
要了解到底发生了什么,查看带有和不具有泛型的代码是有启发性的。任何泛型程序都可以重写为等效的非泛型程序,只需删除泛型并在正确的位置插入强制转换即可。这种转换称为类型擦除。
我们考虑一个简单的实现,其中包含用于获取和设置数组中特定元素的方法,以及用于获取整个数组的方法:Foo<Bar>
class Foo<Bar> {
Bar[] bars = (Bar[])new Object[5];
public Bar get(int i) {
return bars[i];
}
public void set(int i, Bar x) {
bars[i] = x;
}
public Bar[] getArray() {
return bars;
}
}
// in some method somewhere:
Foo<String> foo = new Foo<String>();
foo.set(2, "hello");
String other = foo.get(3);
String[] allStrings = foo.getArray();
类型擦除后,这将变为:
class Foo {
Object[] bars = new Object[5];
public Object get(int i) {
return bars[i];
}
public void set(int i, Object x) {
bars[i] = x;
}
public Object[] getArray() {
return bars;
}
}
// in some method somewhere:
Foo foo = new Foo();
foo.set(2, "hello");
String other = (String)foo.get(3);
String[] allStrings = (String[])foo.getArray();
所以班级内部不再有演员了。但是,调用代码中存在强制转换 - 当获取一个元素并获取整个数组时。为了得到一个元素而进行的强制转换不应该失败,因为我们唯一可以放入数组的东西是,所以我们唯一能得到的东西也是。但是,在获取整个数组时,强制转换将失败,因为数组具有 实际的运行时类型 。Bar
Bar
Object[]
非一般地写,正在发生的事情和问题变得更加明显。特别令人不安的是,强制转换失败不会发生在我们用泛型编写强制转换的类中 - 它发生在使用我们类的其他人的代码中。而其他人的代码是完全安全和无辜的。在我们进行泛型代码转换的时候,它也不会发生 - 它发生在以后,当有人在没有警告的情况下调用时。getArray()
如果我们没有此方法,那么这个类将是安全的。使用此方法,它是不安全的。什么特性使它不安全?它返回为类型,这取决于我们之前所做的“谎言”。由于谎言不是真的,它会引起问题。如果该方法将数组作为类型返回,那么它将是安全的,因为它不依赖于“lie”。getArray()
bars
Bar[]
Object[]
人们会告诉你不要做这样的演员阵容,因为它会导致演员例外,如上所示,在意想不到的地方,而不是在未经检查的演员阵容的原始位置。编译器不会警告您不安全的内容(因为从它的角度来看,给定您告诉它的类型,它是安全的)。因此,这取决于程序员对这个陷阱要勤奋,而不是以不安全的方式使用它。getArray()
但是,我认为这在实践中并不是一个大问题。任何设计良好的 API 都不会向外部公开内部实例变量。(即使有一种方法可以将内容作为数组返回,它也不会直接返回内部变量;它会复制它,以防止外部代码直接修改数组。所以无论如何都不会像现在这样实现任何方法。getArray()