Ответ 1
Вы не можете смешивать общие типы и массивы. У генералов есть проверка времени компиляции, массивы имеют проверку времени выполнения, и эти подходы в основном несовместимы. Сначала я предложил следующее:
@SuppressWarnings("unchecked")
public <T> T[] ConvertToArray(List<T> list)
{
Object[] result = new Object[list.size()];
result = list.toArray(result);
return (T[])result;
}
Это неправильно в незаметном виде, поскольку по крайней мере один другой человек здесь думал, что это сработает! Однако при запуске вы получаете несовместимую ошибку типа, потому что вы не можете передать объект [] в Integer []. Почему мы не можем получить T.class и создать массив подходящего типа? Или new T[]
?
Generics использует стирание типа для сохранения обратной совместимости. Они проверяются во время компиляции, но удаляются из среды выполнения, поэтому байт-код совместим с JVM с предварительным генериком. Это означает, что вы не можете иметь знание класса общей переменной во время выполнения!
Итак, пока вы можете гарантировать, что T[] result
будет иметь тип Integer [] раньше времени, код list.toArray(result);
(или new T[]
или Array.newInstance(T.class, list.size());
) произойдет только во время выполнения, и он не может знать что T есть!
Здесь версия, которая работает, в качестве награды за чтение этой лекции:
public static <T> T[] convertToArray(List<?> list, Class<T> c) {
@SuppressWarnings("unchecked")
T[] result = (T[]) Array.newInstance(c, list.size());
result = list.toArray(result);
return (T[]) result;
}
Обратите внимание, что у нас есть второй параметр для обеспечения класса во время выполнения (а также во время компиляции через дженерики). Вы использовали бы это так:
Integer[] arrayOfIntegers = convertToArray(listOfIntegers, Integer.class);
Неужели это стоит хлопот? Нам все еще нужно подавить предупреждение, так что это определенно безопасно?
Мой ответ - да. Предупреждение, созданное там, есть только "я не уверен" в компиляторе. Пройдя через него, мы можем подтвердить, что это приведение всегда будет успешным - даже если вы помещаете неправильный класс в качестве второго параметра, выдается предупреждение о компиляции.
Основным преимуществом этого является то, что мы централизовали предупреждение в одном месте. Нам нужно только доказать правильность этого места, и мы знаем, что код всегда будет успешным. Чтобы процитировать документацию по Java:
язык разработан, чтобы гарантировать, что если все ваше приложение было скомпилировано без предупреждений без использования javac-источника 1.5, оно безопасно [1]
Итак, теперь, вместо того, чтобы иметь эти предупреждения по всему вашему коду, это просто в одном месте, и вы можете использовать это без необходимости беспокоиться - там значительно снижен риск того, что вы допустили ошибку, используя его.
Вы также можете посмотреть этот SO ответ, который объясняет проблему более подробно, и этот ответ который был моим словом для кроватки при написании этого. Как и уже цитируется Java-документация, другая удобная ссылка, которую я использовал, была этот пост в блоге от Neal Gafter, бывшего старшего технического инженера Sun Microsystems и со-дизайнера 1.4 и 5.0 языковых функций.
И, конечно же, благодаря ShaneC, который справедливо указал, что мой первый ответ не удался во время выполнения!