使用Java泛型理解类型安全异常

时间:2015-04-23 15:28:28

标签: java generics java-8

我有以下代码:

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

这里发生了什么?

2 个答案:

答案 0 :(得分: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 :(得分:0)

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