1) В Интернете и на StackOverflow есть много примеров конкретной проблемы с дженериками и переменными. По сути, это когда у вас есть переменное число аргументов типа параметр типа:
<T> void foo(T... args);
В Java varargs - это синтаксический сахар, который подвергается простой «перезаписи» во время компиляции: параметр типа varargs X...
преобразуется в параметр типа X[]
; и каждый раз, когда вызывается этот метод varargs, компилятор собирает все «переменные аргументы», которые входят в параметр varargs, и создает массив точно так же new X[] { ...(arguments go here)... }
.
Это хорошо работает, когда тип varargs подобен бетону String...
. Когда это переменная типа T...
, например , она также работает, когда T
известно, что это конкретный тип для этого вызова. например , если метод выше , были частью класса Foo<T>
, и у вас есть Foo<String>
ссылка, то вызов foo
на это будет хорошо , потому что мы знаем , T
это String
в тот момент в коде.
Однако он не работает, когда «значение» T
является другим параметром типа. В Java невозможно создать массив типа компонента с параметром типа ( new T[] { ... }
). Таким образом, Java вместо этого использует new Object[] { ... }
(здесь Object
верхняя граница T
; если бы верхняя граница была чем-то другим, это было бы вместо Object
), а затем выдает предупреждение компилятора.
Так что не так с созданием new Object[]
вместо new T[]
или что-то еще? Ну, массивы в Java знают свой тип компонента во время выполнения. Таким образом, переданный объект массива будет иметь неправильный тип компонента во время выполнения.
Вероятно, для наиболее распространенного использования varargs, просто для итерации по элементам, это не проблема (вас не волнует тип массива во время выполнения), так что это безопасно:
@SafeVarargs
final <T> void foo(T... args) {
for (T x : args) {
// do stuff with x
}
}
Однако для всего, что зависит от типа компонента среды выполнения переданного массива, это будет небезопасно. Вот простой пример того, что небезопасно и дает сбой:
class UnSafeVarargs
{
static <T> T[] asArray(T... args) {
return args;
}
static <T> T[] arrayOfTwo(T a, T b) {
return asArray(a, b);
}
public static void main(String[] args) {
String[] bar = arrayOfTwo("hi", "mom");
}
}
Проблема здесь в том, что мы зависим от типа, args
чтобы быть T[]
, чтобы вернуть его как T[]
. Но на самом деле тип аргумента во время выполнения не является экземпляром T[]
.
3) Если ваш метод имеет аргумент типа T...
(где T - любой параметр типа), то:
- Безопасно: если ваш метод зависит только от того факта, что элементы массива являются экземплярами
T
- Небезопасно: если это зависит от того факта, что массив является экземпляром
T[]
Вещи, которые зависят от типа времени выполнения массива, включают: его возвращение как тип T[]
, передачу его в качестве аргумента параметру типа T[]
, получение типа массива с использованием .getClass()
, передачу его методам, которые зависят от типа времени выполнения массива, например, List.toArray()
и Arrays.copyOf()
, и т.д.
2) Различие, которое я упомянул выше, слишком сложно, чтобы его можно было легко различить автоматически.