版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lastsweetop/article/details/83028766
可靠类型是在运行时包含所有完整信息的类型,包括原始类型,非泛型类型,原生类型和无边界通配符的调用。
不可靠类型是编译时类型擦除移除了一些信息,比如不是无边界通配符的其他情况。一个不可靠类型在运行时没有完整可用的信息,
当参数化的变量引用不是该参数化类型的时候就会产生堆污染,如果程序执行某些操作,编译器给出unchecked警告时,就会发生这种情况。如果unchecked警告产生的时候,无论是编译时还是运行时,这个操作的正确性都无法保证。比如混淆了原生类型和参数化类型,或者执行了unchecked的强制转换。
在通常情况下,代码都是同时编译的,编译器会给出unchecked的警告,告诉你堆污染潜在的可能性。但如果代码是分别编译的,你就很难察觉到堆污染的潜在风险。如果你保证你的代码没有警告,你就能保证没有堆污染产生。
包含可变参数的泛型方法会引起堆污染,看下面的例子
public class ArrayBuilder { public static <T> void addToList(List<T> listArg, T... elements) { for (Object x : elements) { listArg.add((T) x); } } public static void faultyMethod(List<String>... l) { Object[] objectArray = l; // Valid objectArray[0] = Arrays.asList(42); String s = l[0].get(0); // ClassCastException thrown here } }
调用类如下:
public class HeapPollutionExample { public static void main(String[] args) { List<String> stringListA = new ArrayList<>(); List<String> stringListB = new ArrayList<>(); ArrayBuilder.addToList(stringListA, "Seven", "Eight", "Nine"); ArrayBuilder.addToList(stringListB, "Ten", "Eleven", "Twelve"); List<List<String>> listOfStringLists = new ArrayList<>(); ArrayBuilder.addToList(listOfStringLists, stringListA, stringListB); ArrayBuilder.faultyMethod(Arrays.asList("Hello!"), Arrays.asList("World!")); } }
当编译的时候 ArrayBuilder.addToList
会给出警告
[unchecked] 对于类型为List<String>[]的 varargs 参数, 泛型数组创建未经过检查
当编译器遇到可变参数方法时,它会把可变参数转换为数组,而java语言并不会创建参数化类型的数组。在方法 ArrayBuilder.addToList
中,编译器会将 T...
转换成 T[]
,但是由于类型擦除,编译器最终会转为 Object[]
,因此就会出现堆栈污染。
接下来的语句将可变参数分配给 Objcet
数组 objectArgs
,
Object[] objectArray = l; // Valid
这条语句有潜在的堆污染,但是编译器却未给出unchecked的警告,因为编译器已经在把 List<String>... l
转换成 List[] l
的时候产生了警告,因此这条语句是有效的,因为 List[]
是 Object[]
的子类型。
因此你接下来给 objectArray
分配任意类型的List对象时,编译器也不会产生警告或者错误,比如下面的语句:
objectArray[0] = Arrays.asList(42);
然后运行的时候就在下面的语句报异常 ClassCastException
:
String s = l[0].get(0);
因为你赋值的是一个 List<Integer>
.
如果你定义了参数化类型的可变参数方法,并且代码中不会产生 ClassCastException
异常,或者因为可变参数处理不当的其他异常,就可以使用注解 @SafeVarargs
来防止放弹出警告。