了解 Java 泛型的类型安全异常[重复]

问题描述 投票:0回答:2

我有以下代码:

public static void main(String[] args) {
    List<String> s = new ArrayList<String>();
    s.add("kshitiz");

    //This is not typesafe. It should blow up at runtime
    List<Integer> i = new ArrayList(s);
    System.out.println(i.get(0));
}

这个程序运行良好并且打印出

kshitiz
。仅当我将最后一行替换为:

时才会失败
System.out.println(i.get(0).getClass());

例外:

Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer

这里发生了什么?

java generics java-8
2个回答
13
投票

我想您知道,泛型类型在运行时消失了。现在,为了了解幕后发生了什么,让我们看看这段代码

public static Class<?> getClass(final Object object) {
    return object.getClass();
}

public static void main(final String[] args) {
    final List<String> s = new ArrayList<String>();
    s.add("kshitiz");

    // This is not typesafe. It should blow up at runtime
    final List<Integer> i = new ArrayList(s);
    System.out.println(getClass(i.get(0)));
    System.out.println(i.get(0).getClass());
}

和 javap -c 的输出

  [...]
  26: getstatic     #8                  // Field java/lang/System.out:Ljava/io/PrintStream;
  29: aload_2
  30: iconst_0
  31: invokeinterface #9,  2            // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
  36: invokestatic  #10                 // Method getClass:(Ljava/lang/Object;)Ljava/lang/Class;
  39: invokevirtual #11                 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
  42: getstatic     #8                  // Field java/lang/System.out:Ljava/io/PrintStream;
  45: aload_2
  46: iconst_0
  47: invokeinterface #9,  2            // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
  52: checkcast     #12                 // class java/lang/Integer
  55: invokevirtual #2                  // Method java/lang/Object.getClass:()Ljava/lang/Class;
  58: invokevirtual #11                 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
  61: return

所以你看到,在第二次调用中,String 被转换为 Integer,而在第一种情况下,它被视为一个对象。 因此,只要像处理任何其他对象一样处理 String,一切都很好,但是一旦调用列表元素类型的方法,该对象就会被强制转换为该类型。


-1
投票

Java 中的泛型在设计上就被破坏了。

在您的

List<Integer> i = new ArrayList(s);
中,您有一个原始的
ArrayList
。这意味着您忽略类型参数,它的工作方式就像它是一个
Object
一样。然后,您(隐式)将
ArrayList
转换为
List<Integer>
,但在运行时不会检查泛型,因此 JVM 不关心不匹配。

当您执行

System.out.println(i.get(0))
时,您不会将
String
转换为
Integer
,因为调用了 'println(Object)` 方法。

System.out.println(i.get(0).getClass())
中,这个神奇的刹车因为某种原因(你可能可以在规格中找到这个原因,但我建议你不要这样做)你的
String
被转换为
Integer

编辑

我猜“15.12.4.5.创建帧,同步,传输控制”描述了她的行为:

如果被调用方法的类型的擦除(§4.6)不同 在其签名中擦除编译时的类型 方法调用的声明(第 15.12.3 节),那么如果有任何 参数值是一个对象,它不是子类的实例或 对应形参类型擦除的子接口 在方法调用的编译时声明中,然后 抛出 ClassCastException。

© www.soinside.com 2019 - 2024. All rights reserved.