2010-06-22 34 views
25

Me gustaría implementar una función con genéricos y varargs.Java 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 la clase que se extienden la clase dada como primer parámetro. Entonces las dos primeras líneas del método principal se compilarían y la tercera generaría un error.

Mi pregunta es: ¿Por qué me sale "seguridad Tipo: Se crea una matriz genérica de la clase para un varargs parámetro" mensaje para las dos primeras líneas?

¿Falta algo aquí?

pregunta adicional: cómo rediseñar para evitar que esta advertencia se muestre en cada línea llamando a la función "doNastyThingsToClasses"? Puedo cambiarlo a "doNastyThingsToClasses (Class < A> parent, Class <?> ... classes)" y deshacerse de las advertencias, pero esto también elimina la comprobación del tipo de tiempo de compilación --- no tan bueno si quería asegurar el uso correcto de esta función. ¿Alguna mejor solución?

Respuesta

34

Como casi siempre, los genéricos de Java de Angelika Langer Preguntas frecuentes explains it in great detail. (Desplácese a "¿Por qué el compilador a veces emite una advertencia no seleccionada cuando invoco un método" varargs "? - la ID no funciona bien.)

Básicamente, termina perdiendo información de una manera peor de lo normal . Sin embargo, otro punto poco dolor en genéricos de Java :(

+13

puedo sugerir que es un punto de dolor en varargs - que no deberían han utilizado matrices. De hecho, la falta de autenticidad debería haber sido reemplazada por listas inmutables (y otras) literales. –

+0

OK, las preguntas frecuentes me lo aclararon. Gran enlace. Gracias. – Chris

+0

@Tom: Sí, eso sin duda ha resuelto este en particular. Matrices y genéricos todavía serían un dolor juntos, por supuesto :) –

0

El segundo argumento Class<? extends A> ... que debe extender la clase que el primer argumento es (ej. Un argumento es un algo Question por lo que el segundo argumento es que se extiende Question.

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

NastyThingsToClasses(Question.class, SomeQuestion.class); // OK
SomeQuestion se extiende Question por lo que es justo juego.

NastyThingsToClasses(Question.class, Object.class, SomeQuestion.class);
Object no se extiende por lo tanto Question error.


esperemos que eso aclare las cosas.

-Brett

+0

El OP preguntaba sobre las advertencias de las dos primeras líneas, no sobre el error –

12
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 construir su proyecto con Java 7.

Bob Lee escribió a proposal para permitir que esta advertencia sea suprimida en el sitio de declaración de método, en lugar del sitio de uso, como parte de Project Coin .

Esta propuesta fue aceptada para JDK7 (aunque la sintaxis cambió ligeramente, a @SuppressWarnings("varargs")); puede, si tiene curiosidad, mirar the commit that added this support to the JDK.

No necesariamente es útil para usted, pero pensé que haría una respuesta separada para que siga viva para los lectores futuros, que tengan la suerte de vivir en un mundo posterior a Java-7.

+1

Ver [respuesta de @ Scott] (http://stackoverflow.com/a/6864043/152061) acerca de @SafeVarargs –

0

OK, así que finalmente terminan lanzando los varargs distancia:

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 poblar la colección utilizado para llevar a los argumentos de la función.

7

Como un lado, la advertencia ahora puede ser suprimida utilizando la nueva anotación @SafeVarargs de Java 7.

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

Mi solución a este problema fue

  1. crear una clase Nastier
  2. quitar ... del doNastyThingsToClasses
  3. hacen método estático doNastyThingsToClasses ninguno
  4. hacer que el nombre corto, como do
  5. devolver esto
  6. movimiento 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 .... :)

+0

No he pensado en el método de encadenamiento ... ¡esta es una muy buena solución! – Chris

+5

Nitpicking, pero 'do' es una palabra clave y no puede ser un nombre de método :-) –