在 Java 中投射泛型列表
Casting generics list in Java
我在转换泛型时发现了一个奇怪的情况。我运行这个代码:
class A { }
class B { }
public class Program {
@SuppressWarnings("unchecked")
public static void main(String[] args) {
List<A> listA = new ArrayList<A>();
List<?> list = listA;
((List<B>)list).add(new B());
for (Object item : listA) {
System.out.println(item.toString());
}
}
}
它编译得很好(只有警告但没有错误)并且 运行 没有任何异常,输出是:
B@88140ed
我是怎么做到的?我的意思是为什么 Java 允许我做这样的事情?
我将 B
class 的实例添加到 A
s?
的列表中
这是非常糟糕的泛型行为。为什么会这样?
顺便说一句,我用 Java 7.
试过了
编辑:
令我惊讶的是Java只用警告通知问题,每个程序员都可以忽略它。我知道 SuppressWarnings
是个坏主意,但为什么 Java 没有拒绝错误或异常的这种行为?
此外,这个警告总是显示,如果你认为你的转换是正确的,你别无选择,只能忽略它。但是,如果您认为这是很好的演员表而忽略了它,但事实并非如此?
It is very bad behaviour of generics. Why it is happening?
因为你强迫它发生在转换中,然后确保你的 @SuppressWarnings("unchecked")
也会忽略警告。
这是你的错,不是泛型机制的错。
您已经通过强制转换和抑制警告击败了 Java 编译时检查。
请注意,感谢 type-erasure,您创建的列表(在幕后)是一个简单的类型未知列表,不包含 运行-time 检查或断言您要放入其中的内容。
每种编程语言都允许您shoot yourself into the foot。
在这种情况下,Java进退两难:它可以将泛型信息保留在字节码中并破坏数百万行现有代码,或者在编译器尽最大努力后静默删除泛型信息检查并保持向后兼容性。
Java 团队决定采用后者并引入类型擦除 - which has its flaws。但是,如果他们破解了数百万行完美无缺(如果类型不完整)的 Java 代码行,人们就会带着干草叉和燃烧的火炬出现......
正如其他人所说,您已经绕过了 java 的类型安全。
我要补充一点,您的代码不会爆炸,因为您的代码不要求元素是任何特定的元素(只是对象)。但是,您是否编码过:
for (A item : listA) { /* whatever */ }
它会编译,但会在运行时抛出 ClassCastException。
为了更安全,您可以修改代码:
for (A item : listA) {/* your code here */}
或
for (Object item : listA) {
if (item instanceof A) {for (A item : listA) {/* your code here*/}
}
即使你修改下面给出的代码,你也会得到 ClassCastException:
for (Object item : listA) {
System.out.println(((A)item).toString()); // Here you will get ClassCastException
}
我在转换泛型时发现了一个奇怪的情况。我运行这个代码:
class A { }
class B { }
public class Program {
@SuppressWarnings("unchecked")
public static void main(String[] args) {
List<A> listA = new ArrayList<A>();
List<?> list = listA;
((List<B>)list).add(new B());
for (Object item : listA) {
System.out.println(item.toString());
}
}
}
它编译得很好(只有警告但没有错误)并且 运行 没有任何异常,输出是:
B@88140ed
我是怎么做到的?我的意思是为什么 Java 允许我做这样的事情?
我将 B
class 的实例添加到 A
s?
这是非常糟糕的泛型行为。为什么会这样?
顺便说一句,我用 Java 7.
试过了编辑:
令我惊讶的是Java只用警告通知问题,每个程序员都可以忽略它。我知道 SuppressWarnings
是个坏主意,但为什么 Java 没有拒绝错误或异常的这种行为?
此外,这个警告总是显示,如果你认为你的转换是正确的,你别无选择,只能忽略它。但是,如果您认为这是很好的演员表而忽略了它,但事实并非如此?
It is very bad behaviour of generics. Why it is happening?
因为你强迫它发生在转换中,然后确保你的 @SuppressWarnings("unchecked")
也会忽略警告。
这是你的错,不是泛型机制的错。
您已经通过强制转换和抑制警告击败了 Java 编译时检查。
请注意,感谢 type-erasure,您创建的列表(在幕后)是一个简单的类型未知列表,不包含 运行-time 检查或断言您要放入其中的内容。
每种编程语言都允许您shoot yourself into the foot。
在这种情况下,Java进退两难:它可以将泛型信息保留在字节码中并破坏数百万行现有代码,或者在编译器尽最大努力后静默删除泛型信息检查并保持向后兼容性。
Java 团队决定采用后者并引入类型擦除 - which has its flaws。但是,如果他们破解了数百万行完美无缺(如果类型不完整)的 Java 代码行,人们就会带着干草叉和燃烧的火炬出现......
正如其他人所说,您已经绕过了 java 的类型安全。
我要补充一点,您的代码不会爆炸,因为您的代码不要求元素是任何特定的元素(只是对象)。但是,您是否编码过:
for (A item : listA) { /* whatever */ }
它会编译,但会在运行时抛出 ClassCastException。
为了更安全,您可以修改代码:
for (A item : listA) {/* your code here */}
或
for (Object item : listA) {
if (item instanceof A) {for (A item : listA) {/* your code here*/}
}
即使你修改下面给出的代码,你也会得到 ClassCastException:
for (Object item : listA) {
System.out.println(((A)item).toString()); // Here you will get ClassCastException
}