Что является, по-видимому, правильным примером кода 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
будет небезопасным.