1) Hay muchos ejemplos en Internet y en StackOverflow sobre el problema particular con los genéricos y los varargs. Básicamente, es cuando tienes un número variable de argumentos de un tipo tipo-parámetro:
<T> void foo(T... args);
En Java, los varargs son un azúcar sintáctico que se somete a una simple "reescritura" en tiempo de compilación: un parámetro de tipo varargs X...
se convierte en un parámetro de tipo X[]
; y cada vez que se realiza una llamada a este método varargs, el compilador recopila todos los "argumentos variables" que van en el parámetro varargs y crea una matriz igual que new X[] { ...(arguments go here)... }
.
Esto funciona bien cuando el tipo varargs es concreto String...
. Cuando se trata de una variable de tipo T...
, también funciona cuando T
se sabe que es un tipo concreto para esa llamada. por ejemplo, si el método anterior era parte de una clase Foo<T>
y usted tiene una Foo<String>
referencia, entonces invocarlo foo
estaría bien porque sabemos que T
está String
en ese punto del código.
Sin embargo, no funciona cuando el "valor" de T
es otro parámetro de tipo. En Java, es imposible crear una matriz de un componente tipo-parámetro tipo ( new T[] { ... }
). Entonces, Java usa new Object[] { ... }
(aquí Object
está el límite superior de T
; si el límite superior fuera algo diferente, sería eso en lugar de Object
), y luego le da una advertencia del compilador.
Entonces, ¿qué hay de malo en crear en new Object[]
lugar de new T[]
o lo que sea? Bueno, las matrices en Java conocen su tipo de componente en tiempo de ejecución. Por lo tanto, el objeto de matriz pasado tendrá el tipo de componente incorrecto en tiempo de ejecución.
Para probablemente el uso más común de varargs, simplemente iterar sobre los elementos, esto no es un problema (no le importa el tipo de tiempo de ejecución de la matriz), por lo que esto es seguro:
@SafeVarargs
final <T> void foo(T... args) {
for (T x : args) {
// do stuff with x
}
}
Sin embargo, para cualquier cosa que dependa del tipo de componente de tiempo de ejecución de la matriz pasada, no será seguro. Aquí hay un ejemplo simple de algo que no es seguro y se bloquea:
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");
}
}
El problema aquí es que dependemos del tipo de args
ser T[]
para devolverlo como T[]
. Pero en realidad el tipo de argumento en tiempo de ejecución no es una instancia de T[]
.
3) Si su método tiene un argumento de tipo T...
(donde T es cualquier parámetro de tipo), entonces:
- Seguro: si su método solo depende del hecho de que los elementos de la matriz son instancias de
T
- Inseguro: si depende del hecho de que la matriz es una instancia de
T[]
Las cosas que dependen del tipo de tiempo de ejecución de la matriz incluyen: devolverlo como tipo T[]
, pasarlo como argumento a un parámetro de tipo T[]
, obtener el tipo de matriz usando .getClass()
, pasarlo a métodos que dependen del tipo de tiempo de ejecución de la matriz, como List.toArray()
y Arrays.copyOf()
etc.
2) La distinción que mencioné anteriormente es demasiado complicada para poder distinguirla automáticamente.