Что является, по-видимому, правильным примером кода Java, вызывающего загрязнение кучи?

Я пытаюсь решить, что делать каждый раз, когда я получаю предупреждение о загрязнении Java-кучей при использовании параметризованных varargs, таких как

public static <T> LinkedList<T> list(T... elements) {
    ...
}

Мне кажется, что если я уверен, что не буду использовать какие-то странные броски в своих методах, я должен просто использовать @SafeVarargs и двигаться дальше. Но правильно ли это, или мне нужно быть более осторожным? Есть ли, по-видимому, правильный код, который на самом деле небезопасен при использовании параметризованных varargs?

Читая о предмете, я замечаю, что приведенные примеры являются довольно искусственными. Например, Java-документация показывает следующий ошибочный метод:

  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
  }

который является дидактическим, но довольно нереалистичным; опытные программисты вряд ли будут писать код, делающий такие вещи. Другой пример -

Pair<String,String>[] method(Pair<String,String>... lists) { 
   Object[] objs = lists; 
   objs[0] = new Pair<String,String>("x","y");  
   objs[1] = new Pair<Long,Long>(0L,0L);  // corruption !!! 
   return lists; 
} 

который снова довольно явно смешивает типы нереалистичным образом.

Итак, существуют ли более тонкие случаи, когда загрязнение кучи происходит при параметризованных varargs? Я оправдан в использовании @SafeVarargs, если я не перебрасываю переменные таким образом, что теряет информацию о типе или неправильно смешивает типы? Другими словами, могу ли я оправдать это предупреждение как не очень важную формальность?

Ответ 1

Хороший вопрос. Это беспокоило меня довольно долгое время. Здесь есть две вещи: вам не нужен фактический тип времени выполнения элементов в массиве, например пример, который вы показали:
public static <T> LinkedList<T> list(T... elements) {
    // suppose you iterate over them and add
}

Здесь @SafeVarargs хорошо, безопасно.

И второй - это то, где вы заботитесь о типе времени выполнения элементов в массиве (даже если это случайно). Массивы в java не могут быть общими, поэтому вы не можете создать тип T [] ts = new T[10], но вы можете объявить тип T[] ts..., и поскольку массивы являются ковариантными, вы можете применить Object[] к T[], если вы знайте соответствия типов.

Все это становится интересным, если передать общий массив:

// create a single element "generic" array
static <T> T[] singleElement(T elem) {
    @SuppressWarnings("unchecked")
    T[] array = (T[]) new Object[] { elem };
    return self(array);
}

// @SafeVarargs
static <T> T[] self(T... ts) {
    return ts;
}

Вызов этого с помощью Integer[] ints = singleElement(1); выглядит совершенно законным, но будет прерываться во время выполнения, поэтому размещение @SafeVarargs будет небезопасным.