Java Object类与泛型

526 阅读2分钟

问题

无意中看到垠神(王垠)的博客一道java面试题,由此开始了自己的思考。其实就是分析一段代码,其问题出在哪里,代码如下。

public class TypeCheck {
    public static void f() {
        String[] a = new String[2];
        Object[] b = a;
        a[0] = "hi";
        b[1] = Integer.valueOf(42);
    }
}

猜测

出问题的代码在

b[1] = Integer.valueOf(42);

因为b引用的是一个字符串类型的数组,给字符串类型的数组赋整数类型的值时,由于数组会对其持有的类型做检查,因此会抛出运行时异常。

证实

编译上述代码,得到.class文件。执行

 javap -c TypeCheck.class

得到字节码的文本。

public class generic.TypeCheck {
  public generic.TypeCheck();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void f();
    Code:
       0: iconst_2
       1: anewarray     #2                  // class java/lang/String
       4: astore_0
       5: aload_0
       6: astore_1
       7: aload_0
       8: iconst_0
       9: ldc           #3                  // String hi
      11: aastore
      12: aload_1
      13: iconst_1
      14: bipush        42
      16: invokestatic  #4                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      19: aastore
      20: return
}

这里我们重点关注

19: aastore

根据oralce的指令说明官方文档,对 aastore 指令的描述。

Otherwise, if arrayref is not null and the actual type of value is not assignment compatible (JLS §5.2) with the actual type of the components of the array, aastore throws an ArrayStoreException.

数组必须是类型确定的,在数组中存储数值时,如果值的实际类型和预期类型不符,则会抛出异常。

思考

Java设计者对数组的此类设计,可以防止因数据类型不一致,造成的异常。但是问题在于为何上述代码能通过编译?原因在于:java se5之前,没有泛型,需要有一种方式使类或者方法具备抽象的表达能力。

上述代码写成:

public class TypeCheck {
    public static void f() {
        List<String>a = new ArrayList<>();
        List<String>b = a;
        a.add("hi");
        b.add(Integer.valueOf(42));//不会通过编译
    }
}

可以更早的暴露问题。利用参数化类型,由编译器来对插入的类型做校验,确保了数据的一致性。