java generics types variadic-functions

Genéricos de Java y Varargs.



generics types (6)

Como casi siempre, las preguntas frecuentes sobre los genéricos de Java de Angelika Langer lo explican con gran detalle . (Desplácese hasta "¿Por qué el compilador a veces emite una advertencia sin marcar cuando invoco un método" varargs "?" La ID no funciona bien).

Básicamente, terminas perdiendo información de una manera peor de lo normal. Otro punto más de dolor en los genéricos de Java :(

Me gustaría implementar una función con genéricos y varargs.

public class Question { public static <A> void doNastyThingsToClasses(Class<A> parent, Class<? extends A>... classes) { /*** something here ***/ } public static class NotQuestion { } public static class SomeQuestion extends Question { } public static void main(String[] args) { doNastyThingsToClasses(Object.class, Question.class, SomeQuestion.class); // OK doNastyThingsToClasses(Question.class, SomeQuestion.class); // OK doNastyThingsToClasses(Question.class, Object.class, SomeQuestion.class); // compilation failure } }

La intención aquí es afirmar que todos los parámetros pasados ​​a esta función son objetos de clase que extienden la clase dada como primer parámetro. Así que las dos primeras líneas del método principal se compilarían y la tercera generaría un error.

Mi pregunta es: ¿Por qué aparece el mensaje "Seguridad de tipo: se crea una matriz genérica de Clase para un parámetro de varargs" para las dos primeras líneas?

¿Me estoy perdiendo de algo?

Pregunta adicional: ¿Cómo rediseñarlo para evitar que se muestre esta advertencia en cada línea que llame a la función "doNastyThingsToClasses"? Puedo cambiarlo a "doNastyThingsToClasses (Clase <A> padre, Clase <?> ... clases)" y deshacerme de las advertencias, pero esto también elimina la verificación de tipos de tiempo de compilación, no tan bueno si quisiera Asegurar el uso correcto de esta función. ¿Alguna solución mejor?


Como nota aparte, la advertencia ahora se puede suprimir utilizando la nueva anotación @SafeVarargs de Java 7.

@SafeVarargs public static <A> void func( Class<A> parent, Class<? extends A>... classes ) { // Do func... }


El segundo argumento Class<? extends A> Class<? extends A> ... que debe extender la clase en la que se encuentra el primer argumento (por ejemplo, el argumento uno es una Question por lo que el segundo argumento es algo que extiende la Question .

El desglose:
NastyThingsToClasses(Object.class, Question.class, SomeQuestion.class); // OK
Todo extiende a Object por lo que el segundo argumento es correcto.

NastyThingsToClasses(Question.class, SomeQuestion.class); // OK
SomeQuestion extiende la Question así que eso es juego justo.

NastyThingsToClasses(Question.class, Object.class, SomeQuestion.class);
Object no extiende la Question tanto, el error.


Esperemos que eso aclarara las cosas.

-Brett


La respuesta de Jon Skeet es (por supuesto) correcta; Lo ampliaré un poco al señalar que PUEDES deshacerte de esta advertencia, con un gran ''si''. Puede evitar esta advertencia SI está dispuesto a comprometerse a que su proyecto se compile utilizando Java 7.

Bob Lee escribió una propuesta para permitir que esta advertencia se suprima en el sitio de declaración de métodos, en lugar de usar el sitio, como parte de Project Coin .

Esta propuesta fue aceptada para JDK7 (aunque la sintaxis cambió ligeramente, a @SuppressWarnings("varargs") ); puede, si tiene curiosidad, ver el compromiso que agregó este soporte al JDK .

No es necesariamente útil para usted, pero pensé que sería una respuesta por separado para que viva para futuros lectores, quienes pueden tener la suerte de vivir en un mundo posterior a Java-7.


Mi solución a este problema fue

  1. crear una clase nastier
  2. eliminar ... de doNastyThingsToClasses
  3. hacer doNastyThingsToClasses método estático
  4. hacer el nombre corto, como hacer
  5. devuelve esto
  6. mover argumentos repetitivos a las propiedades de clase

    class Nastier { private final Class<A> parent; public Nastier(Class<A> parent) { this.parent = parent; } public <A, C extends A> Nastier do(Class<? extends A> clazz) { System.out.println(clazz); return this; } } public static void main(String[] args) { Nastier nastier = new Nastier(Object.class); nastier.do(Question.class).do(SomeQuestion.class).do(NotQuestion.class); }

Creo que el código se ve limpio y estoy feliz .... :)


OK, finalmente termino tirando los varargs:

public class Question { public static <A, C extends A> void doNastyThingsToClasses(Class<A> parent, List<Class<? extends A>> classes) { /******/ for(Class<? extends A> clazz : classes) { System.out.println(clazz); } } public static class NotQuestion { } public static class SomeQuestion extends Question { } public static void main(String[] args) { ArrayList<Class<? extends Object>> classes = new ArrayList<Class<? extends Object>>(); classes.add(Question.class); classes.add(SomeQuestion.class); classes.add(NotQuestion.class); doNastyThingsToClasses(Object.class, classes); ArrayList<Class<? extends Question>> clazzes = new ArrayList<Class<? extends Question>>(); clazzes.add(Question.class); clazzes.add(SomeQuestion.class); clazzes.add(NotQuestion.class); // yes, this will _not_ compile doNastyThingsToClasses(Question.class, clazzes); } }

El único defecto es el código largo para rellenar la colección utilizada para llevar los argumentos de la función.