Recientemente me he encontrado con la @SafeVarargs
anotación de Java . Buscar en Google lo que hace que una función variada en Java sea insegura me dejó bastante confundido (¿envenenamiento en el montón? ¿Tipos borrados?), Así que me gustaría saber algunas cosas:
¿Qué hace que una función Java variada sea insegura en el
@SafeVarargs
sentido (preferiblemente explicada en forma de un ejemplo en profundidad)?¿Por qué se deja esta anotación a discreción del programador? ¿No es esto algo que el compilador debería poder verificar?
¿Hay algún estándar que uno deba cumplir para garantizar que su función sea realmente segura? Si no, ¿cuáles son las mejores prácticas para garantizarlo?
retType myMethod(Arg first, Arg... others)
. Si no especificafirst
, se permite una matriz vacía, y es muy posible que tenga un método con el mismo nombre con el mismo tipo de retorno sin argumentos, lo que significa que la JVM tendrá dificultades para determinar qué método debe llamarse.Respuestas:
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:
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 tipoX[]
; 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 quenew X[] { ...(arguments go here)... }
.Esto funciona bien cuando el tipo varargs es concreto
String...
. Cuando se trata de una variable de tipoT...
, también funciona cuandoT
se sabe que es un tipo concreto para esa llamada. por ejemplo, si el método anterior era parte de una claseFoo<T>
y usted tiene unaFoo<String>
referencia, entonces invocarlofoo
estaría bien porque sabemos queT
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 usanew Object[] { ... }
(aquíObject
está el límite superior deT
; si el límite superior fuera algo diferente, sería eso en lugar deObject
), y luego le da una advertencia del compilador.Entonces, ¿qué hay de malo en crear en
new Object[]
lugar denew 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:
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:
El problema aquí es que dependemos del tipo de
args
serT[]
para devolverlo comoT[]
. Pero en realidad el tipo de argumento en tiempo de ejecución no es una instancia deT[]
.3) Si su método tiene un argumento de tipo
T...
(donde T es cualquier parámetro de tipo), entonces:T
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 tipoT[]
, obtener el tipo de matriz usando.getClass()
, pasarlo a métodos que dependen del tipo de tiempo de ejecución de la matriz, comoList.toArray()
yArrays.copyOf()
etc.2) La distinción que mencioné anteriormente es demasiado complicada para poder distinguirla automáticamente.
fuente
FOO<T extends Something>
¿sería seguro devolver la T [] de un método varargs como una matriz deSomthing
s?@SafeVarargs
es cuando lo único que hace con la matriz es pasarlo a otro método que ya está anotado (por ejemplo: con frecuencia me encuentro escribiendo métodos vararg que existen para convertir su argumento en una lista usandoArrays.asList(...)
y pasarlo a otro método; siempre se puede anotar este caso@SafeVarargs
porqueArrays.asList
tiene la anotación).@SafeVarargs
menos que el método seastatic
ofinal
, porque de lo contrario podría anularse en una clase derivada con algo inseguro.private
métodos que tampoco se pueden anular.Para las mejores prácticas, considere esto.
Si tienes esto:
Cámbialo a esto:
He descubierto que generalmente solo agrego varargs para que sea más conveniente para mis llamantes. Casi siempre sería más conveniente para mi implementación interna usar a
List<>
. Entonces, para aprovecharArrays.asList()
y asegurarme de que no hay manera de que pueda introducir la contaminación del montón, esto es lo que hago.Sé que esto solo responde a tu # 3. newacct ha dado una excelente respuesta para el n. ° 1 y n. ° 2 anteriores, y no tengo suficiente reputación para dejar esto como un comentario. :PAGS
fuente