堆污染简单参数

时间:2017-06-07 10:14:58

标签: java generics compiler-warnings

我有以下功能:

public static <T> List<T> list(T... xs) {
    final List<T> lst = new ArrayList<T>();
    for (final T x : xs) {
        lst.add(x);
    }

    return lst;
}

它的用法很简单:

   List<Integer> ints = list(1, 2, 3, 4)

编译器为此列表提供了以下警告

  

&#34; TypeSafety:via varargs参数的潜在堆污染

我试图找到它意味着什么,但我发现的所有解释都是参数的函数,这些参数本身是参数化的,例如

f(List&lt; T&gt; ... xss)。

虽然我有泛型非参数化参数的功能。

请解释一下我的功能有什么潜在的问题因为我找不到。

2 个答案:

答案 0 :(得分:3)

Java中的Varargs是一种合成糖。 T... xsT[] xs相同。因此,实际上你有一个带参数化参数(数组)的函数。

回到你的问题。当您将List<String>作为泛型类型传递给方法时,让我们考虑一下这种情况。然后你有一个List<String>元素数组作为你的方法的参数。由此产生了两个重要问题,并导致代码中存在可能的缺陷。

  1. 数组是协变的,这意味着编译器将允许这项任务:Object[] arr = xs
  2. 由于编译期间的通用擦除,数组包含原始List元素,遗憾的是,Java中没有办法保证放在数组中的元素正好是List<String>。因此,如果您在运行时抛出List<Integer> java.lang.ArrayStoreException 而不是
  3. 这一切都会导致你可能产生堆污染的情况。请参阅以下示例:

    public static <T> List<T> list(T... xs) {
        final List<T> lst = new ArrayList<T>();
        for (final T x : xs) {
            lst.add(x);
        }
        Object[] arr = xs; //arrays are covariant, we can do this
        arr[0] = Arrays.asList(4); //<--------heap pollution
        return lst;
    }
    public static void main(String[] args) {
        List[] arr = { Arrays.asList("one"), Arrays.asList("two"), Arrays.asList("three") };
        List<List<String>> l = list(arr);
        for (List list : arr) {
            System.out.println(list.get(0));
        }
    }
    

    输出:

    4
    two
    three
    

    以下是关于该主题的非常好的解释: http://www.angelikalanger.com/GenericsFAQ/FAQSections/ProgrammingIdioms.html#FAQ300

    官方Java指南: https://docs.oracle.com/javase/8/docs/technotes/guides/language/non-reifiable-varargs.html

答案 1 :(得分:2)

@ VasiliyVlasov&#39; s answer很好地解释了警告的原因。但是在你的问题的背景下,list()方法确实是安全的,因为它不会做任何不安全的操作。该警告只表示存在潜在问题&#34;因为编译器无法确定您正在调用的方法是否安全实施。为避免出现此警告,您可以使用@SafeVarargs进行标记来保证方法的安全性。有关详细信息,请参阅JavadocJLS