java遗珠之泛型不可靠类型

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: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来防止放弹出警告。

展开阅读全文

没有更多推荐了,返回首页