crear - Anotación Java SafeVarargs, ¿existe una práctica estándar o mejor?
crear annotation en java (1)
1) Hay muchos ejemplos en Internet y en StackOverflow sobre el problema particular con genéricos y varargs. Básicamente, es cuando tienes un número variable de argumentos de un tipo de tipo de parámetro:
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 varargs de tipo 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 como new X[] { ...(arguments go here)... }
.
Esto funciona bien cuando el tipo varargs es concreto como String...
Cuando se trata de una variable de tipo como T...
, también funciona cuando se sabe que T
es un tipo concreto para esa llamada. por ejemplo, si el método anterior era parte de una clase Foo<T>
, y tienes una referencia Foo<String>
, entonces llamar a foo
estaría bien porque sabemos que T
es 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 tipo de componente de tipo de tipo ( new T[] { ... }
). Así que Java en su lugar usa el new Object[] { ... }
(aquí Object
es el límite superior de T
; si el límite superior fuera algo diferente, sería en lugar de Object
), y luego le da una advertencia del compilador.
Entonces, ¿qué hay de malo en crear un new Object[]
lugar de un 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 para 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 es seguro:
@SafeVarargs
final 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 es inseguro 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
para 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 en el tipo de tiempo de ejecución de la matriz, como List.toArray()
y Arrays.copyOf()
, etc.
2) La distinción que mencioné arriba es demasiado complicada para ser fácilmente distinguida automáticamente.
Recientemente me encontré con la anotación java @SafeVarargs . Buscar en Google lo que hace que una función variada en Java no sea segura me dejó bastante confundido (¿tipo de intoxicación por montón?), Así que me gustaría saber algunas cosas:
¿Qué hace que una función variada de Java sea insegura en el sentido @SafeVarargs (preferiblemente explicada en forma de un ejemplo en profundidad)?
¿Por qué esta anotación queda a discreción del programador? ¿No es esto algo que el compilador debería poder verificar?
¿Hay alguna norma a la que se deba adherir para garantizar que su función sea efectivamente segura? Si no, ¿cuáles son las mejores prácticas para garantizarlo?