由于问题中提到了我,我会插话。
基本上,如果你不将这个数组变量暴露给类的外部,它不会造成任何问题。 (有点像,在维加斯发生的事情留在维加斯。)
数组的实际运行时类型是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() 时发生,而没有警告。
如果我们没有这个getArray() 方法,那么这个类将是安全的。用这种方法,是不安全的。什么特点使它不安全?它以Bar[] 类型返回bars,这取决于我们之前制作的“谎言”。由于谎言不是真的,它会引起问题。如果该方法将数组返回为 Object[] 类型,那么它将是安全的,因为它不依赖于“谎言”。
人们会告诉你不要进行这样的转换,因为它会在如上所示的意外位置导致转换异常,而不是在未经检查的转换所在的原始位置。编译器不会警告您 getArray() 不安全(因为从它的角度来看,鉴于您告诉它的类型,它是安全的)。因此,程序员必须认真对待这个陷阱,不要以不安全的方式使用它。
但是,我认为这在实践中并不是一个大问题。任何设计良好的 API 都不会将内部实例变量暴露给外部。 (即使有方法将内容作为数组返回,它也不会直接返回内部变量;它会复制它,以防止外部代码直接修改数组。)所以不会像getArray()那样实现任何方法无论如何。