自从我在问题中被提及以来,我会插话。
基本上,如果不将此数组变量暴露给类的外部对象,则不会造成任何问题。(有点像,在拉斯维加斯发生的事情留在拉斯维加斯。)
数组的实际运行时类型为
Object[]。因此将其放入类型变量
Bar[]实际上是一个“谎言”,因为
Object[]它不是
Bar[](除非
Object是
Bar)的子类型。但是,如果该谎言停留在班级内部,则可以,因为它
Bar已被删除到
Object班级内部。(下界的
Bar是
Object在这个问题上,在下界的情况
Bar是另一回事,更换所有出现
Object在这次讨论与任何该界是。)但是,如果这个谎言被莫名其妙地暴露于外(最简单的示例
bars直接将变量作为type
返回
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()方法,则此类将是安全的。用这种方法,是不安全的。什么特征使其不安全?它
bars以type
返回
Bar[],这取决于我们之前所做的“谎言”。由于谎言不是真的,因此会引起问题。如果该方法改为将数组返回为type
Object[],那么它将是安全的,因为它不依赖于“ lie”。
人们会告诉您不要像这样进行强制转换,因为它会导致意外异常(如上所示),而不是未经检查的强制转换的原始位置,导致强制转换异常。编译器不会警告您这
getArray()是不安全的(因为从它的角度来看,给定您所告诉的类型,它是安全的)。因此,这取决于程序员对这种陷阱的勤奋工作,而不是以不安全的方式使用它。
但是,我认为这在实践中并不是一个大问题。任何设计良好的API都不会将内部实例变量暴露给外部。(即使有一种方法将内容作为数组返回,它也不会直接返回内部变量;而是会复制它,以防止外部代码直接修改该数组。)因此,
getArray()无论如何都不会实现任何方法。



