1) Ci sono molti esempi su Internet e su StackOverflow sul problema particolare con generici e varargs. Fondamentalmente, è quando hai un numero variabile di argomenti di un tipo di parametro-tipo:
<T> void foo(T... args);
In Java, varargs è uno zucchero sintattico che subisce una semplice "riscrittura" in fase di compilazione: un parametro varargs di tipo X...
viene convertito in un parametro di tipo X[]
; e ogni volta che viene fatta una chiamata a questo metodo varargs, il compilatore raccoglie tutti gli "argomenti variabili" che vanno nel parametro varargs e crea un array proprio come new X[] { ...(arguments go here)... }
.
Funziona bene quando il tipo di varargs è simile al concreto String...
. Quando è una variabile di tipo come T...
, funziona anche quando T
è noto per essere un tipo concreto per quella chiamata. ad esempio se il metodo sopra faceva parte di una classe Foo<T>
e hai un Foo<String>
riferimento, allora chiamarlo foo
andrebbe bene perché sappiamo che T
è String
a quel punto nel codice.
Tuttavia, non funziona quando il "valore" di T
è un altro parametro di tipo. In Java, è impossibile creare una matrice di un componente di tipo-parametro tipo ( new T[] { ... }
). Quindi Java invece usa new Object[] { ... }
(qui Object
è il limite superiore di T
; se il limite superiore fosse qualcosa di diverso, sarebbe quello invece di Object
), e quindi ti dà un avviso del compilatore.
Quindi cosa c'è di sbagliato nel creare new Object[]
invece new T[]
o altro? Bene, gli array in Java conoscono il loro tipo di componente in fase di esecuzione. Pertanto, l'oggetto array passato avrà il tipo di componente errato in fase di esecuzione.
Per l'uso probabilmente più comune di varargs, semplicemente per scorrere gli elementi, questo non è un problema (non ti interessa il tipo di runtime dell'array), quindi è sicuro:
@SafeVarargs
final <T> void foo(T... args) {
for (T x : args) {
// do stuff with x
}
}
Tuttavia, per tutto ciò che dipende dal tipo di componente di runtime dell'array passato, non sarà sicuro. Ecco un semplice esempio di qualcosa che non è sicuro e si blocca:
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");
}
}
Il problema qui è che dipendiamo dal tipo di args
essere T[]
per restituirlo come T[]
. Ma in realtà il tipo di argomento in fase di runtime non è un'istanza di T[]
.
3) Se il tuo metodo ha un argomento di tipo T...
(dove T è un parametro di tipo), allora:
- Sicuro: se il metodo dipende solo dal fatto che gli elementi dell'array sono istanze di
T
- Non sicuro: se dipende dal fatto che l'array è un'istanza di
T[]
Le cose che dipendono dal tipo di runtime dell'array includono: restituirlo come tipo T[]
, passarlo come argomento a un parametro di tipo T[]
, ottenere il tipo di array usando .getClass()
, passarlo a metodi che dipendono dal tipo di runtime dell'array, come List.toArray()
e Arrays.copyOf()
, eccetera.
2) La distinzione che ho menzionato sopra è troppo complicata per essere facilmente distinta automaticamente.