首页 > 代码库 > JAVA泛型容器的类型检查
JAVA泛型容器的类型检查
泛型容器是通过指定容器包含对象的类型,由编译器保证对象类型的正确性,在编译阶段就能检查出类型错误。如下列将List<Long>对象longList赋予一个List<GenericTest>对象gtList,会报编译错误。
public class GenericTest
{
public static List<Long> longList = Arrays.asList (1L ,2L);
public static void main(String args[]){
//下面这条语句编译会报错
List<GenericTest> gtList=longList ;
}
}
如果这只是在编译检查,那么我们是否可以绕过编译检查呢?我们先将List<Long>对象longList赋予一个raw List变量rawList,然后再将这个rawList强制类型转换成List<GenericTest>类型。结果编译通过了,并且运行时也没有报错。我们成功地骗过了编译器。
public class GenericTest
{
public static List<Long> longList = Arrays.asList (1L ,2L);
public static void main(String args[]){
List rawList= longList;
//这个是可以编译通过,成功的骗过了编译器
List<GenericTest> gtList= rawList;
}
}
接着,如果我们使用List<GenericTest>类型的变量gtList,那会发生什么事。注意gtList引用的容器里面放的实际上是Long类型对象。这会发生什么事,会引用一段错误的内存吗?结果我们发现String result = gtList.get(0).stringValue;语句正确地在运行时抛出 java.lang.ClassCastException异常。分析 main函数的字节码可知, 在该语句之前插入了类型转换检查字节码,导致了这个异常的抛出。看来Java设计者已经考虑到这一点,在泛型容器对象使用前加上了类型检查,防止这样的情况。
public class GenericTest
{
public static List<Long> longList = Arrays.asList (1L ,2L);
public StringstringValue ="ss";
public static void main(String args[]){
List rawList= longList;
//这个是可以编译通过,成功的骗过了编译器
List<GenericTest> gtList= rawList;
//会在运行时抛出 java.lang.ClassCastException
String result =gtList.get (0).stringValue;
}
}
字节码分析如下:
0 getstatic #28<variable/GenericTest.longList> //获得类变量longList, 并放入栈顶
3 astore_1 //将栈顶引用放入第一个本地变量 rawList
4 aload_1 //将第一个本地变量rawList,放入栈顶
5 astore_2 //将栈顶引用放入第二个本地变量tgList,这里我们可以看到将rawList赋值给tgList,没有任何类型检测,所以运行通过
6 aload_2
7 iconst_0
8 invokeinterface #43<java/util/List.get> count 2
13 checkcast #1 <variable/GenericTest> // 检测类型转换,当我们使用容器中对象时,会有类型检测,导致抛出java.lang.ClassCastException
16 getfield #37<variable/GenericTest.stringValue> // 获得实例属性,并放入栈顶
19 astore_3 //将栈顶引用放入第三个本地变量 result
20 return
JAVA泛型容器的类型检查