1、问题背景
今天和新来的同事小孙讨论到限制容器中数据类型的问题,小孙一脸的不屑,这还不简单,加个泛型不就可以了!于是他就用以下代码向我演示了一遍:
public class GenericityTest {
public static void main(String[] args) {
ArrayList<Integer> intArr = new ArrayList<>();
intArr.add(1);
/*
* Required type: Integer
* Provided: String
*/
//intArr.add("abc"); // 添加失败
// 打印 intArr
for (int i = 0; i < intArr.size(); i++) {
System.out.println(intArr.get(i));
}
}
}
2、反驳说明
看起来好像是这么一回事,可是这怎么能行呢?我必须要让他知道社会的残酷。
我找到小孙:“你可以帮我看看这段代码吗?为什么我加了泛型,可是把字符串也给加进去了?”。
public class GenericityMain {
public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
ArrayList<Integer> intArr = new ArrayList<>();
intArr.add(1);
Class<? extends ArrayList> arrClazz = intArr.getClass();
Method arrAddMethod = arrClazz.getMethod("add", Object.class);
// Method arrAddMethod = arrClazz.getDeclaredMethod("add", Object.class);
arrAddMethod.invoke(intArr, "abc");
// 打印 intArr
for (int i = 0; i < intArr.size(); i++) {
System.out.println(intArr.get(i)); // 1 abc
}
}
}
小孙:“这,这,这......”。
3、原因说明
在使用泛型时,我们要知道因为什么引入了泛型?
Java泛型是在 JDK5 中引入的,他提供了编译时类型安全检测机制。由这我们就知道了,泛型只是在编译时起作用,那就简单了,我们可以通过一些手段跳过编译期不就可以了吗?
4、JVM说明
找到类 GenericityTest的编译文件 GenericityTest.class,我们来反汇编看看 JVM 编码是怎么样的?
$ javap -c GenericityTest
警告: 二进制文件GenericityTest包含com.hz.genericity.GenericityTest
Compiled from "GenericityTest.java"
public class com.hz.genericity.GenericityTest {
public com.hz.genericity.GenericityTest();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: new #2 // class java/util/ArrayList
3: dup
4: invokespecial #3 // Method java/util/ArrayList."<init>":()V
7: astore_1
8: aload_1
9: iconst_1
10: invokestatic #4 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
13: invokevirtual #5 // Method java/util/ArrayList.add:(Ljava/lang/Object;)Z
16: pop
17: iconst_0
18: istore_2
19: iload_2
20: aload_1
21: invokevirtual #6 // Method java/util/ArrayList.size:()I
24: if_icmpge
27: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
30: aload_1
31: iload_2
32: invokevirtual #8 // Method java/util/ArrayList.get:(I)Ljava/lang/Object;
35: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
38: iinc 2, 1
41: goto 19
44: return
}
说明:我们主要看 13 行 #5 add:(Ljava/lang/Object)这里我们看到,在JVM中还是Object,并不是我们认为的 Integer。