2

I have following function:

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;
}

Its usage is simple:

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

Compiler gives me following warning for this list

"TypeSafety: potential heap pollution for via varargs parameter

I tried to find what it means but all explanastions I found were for functions of parameters that are themselves parametrized e.g.

f(List<T>... xss).

While I have function of generic non parametrized parameter.

Please explain me what is the potential problem with my function because I can not find any.

Abdul
  • 2,002
  • 7
  • 31
  • 65
Trismegistos
  • 3,821
  • 2
  • 24
  • 41

2 Answers2

3

Varargs in Java is a syntatic sugar. T... xs is the same as T[] xs. Therefore actually you have a function with parametrized parameter (the array).

Back to your question. Let's consider the situation, when you pass List<String> as generic type to the method. Then you have an array of elements of List<String> as an argument to your method. Two important issues arise because of this and lead to possible flaw in the code.

  1. Arrays are covariant and it means that the compilator will allow this assignment: Object[] arr = xs
  2. Because of generic erasure during compile time the array contains raw List elements and, unfortunately, there is no way in Java to guarantee, that the element you put in the array is exactly List<String>. So if you put List<Integer> java.lang.ArrayStoreException will not be thrown at runtime.

This alltogether leads to a situation when you may produce heap polution. Please, see the following example:

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));
    }
}

Output:

4
two
three

Here is a really good one explanation on the topic: http://www.angelikalanger.com/GenericsFAQ/FAQSections/ProgrammingIdioms.html#FAQ300

Official Java Guide: https://docs.oracle.com/javase/8/docs/technotes/guides/language/non-reifiable-varargs.html

Vasiliy Vlasov
  • 3,316
  • 3
  • 17
  • 20
3

@VasiliyVlasov's answer nicely explains the reason for the warning. But in the context of your question, the list() method is indeed safe because it doesn't do any unsafe operations. The warning only says there's a "potential problem" because the compiler can't be sure the method you're calling is implemented safely. To avoid the warning, you can vouch for the safety of your method by marking it with @SafeVarargs. See the Javadoc and the JLS for more details.

shmosel
  • 49,289
  • 6
  • 73
  • 138