2009-04-14 5 views
45

Quiero crear una matriz de Clases, cada una representando un tipo que está disponible en el sistema que estoy creando. Todas las clases implicadas son subclases de una superclase común. Así que me gustaría hacer:¿Cómo uso los genéricos con una variedad de clases?

Class<? extends SuperClass>[] availableTypes = { SubClass1.class, SubClass2.class }; 

Esto me da el error:

Cannot create a generic array of Class<? extends SuperClass>. 

consigo el mismo mensaje si intento para calificar la creación de la matriz en el lado derecho de la inicialización:

Class<? extends SuperClass>[] availableTypes = Class<? extends SuperClass>[] { SubClass1.class, SubClass2.class }; 

puedo obtener el código para compilar si elimino los genéricos calificaciones:

Class[] availableTypes = { SubClass1.class, SubClass2.class }; 

Pero cuando me siento la advertencia genéricos:

clase es un tipo de prima. Las referencias al tipo genérico Clase deben ser parametrizadas.

Lo estoy intentando; ¡Lo estoy intentando! :) Además, en este punto, incluso si esto no provocó una advertencia, pierdo una parte de la interfaz que estaba tratando de definir. No quiero simplemente devolver una variedad de clases arbitrarias; ¡Quiero devolver un conjunto de clases que son subclases de una SuperClass en particular!

Eclipse tiene algunas herramientas bastante poderosas para averiguar qué parámetros usar para corregir las declaraciones de genéricos, pero en este caso se cae, ya que tiende a hacer cuando se trata de clase. El procedimiento "Argumentos de tipo genérico de Infer" que ofrece no cambia el código en absoluto, dejando la advertencia.

pude evitar esto usando una colección en su lugar:

List<Class<? extends SuperClass>> availableTypes = new List<Class<? extends SuperClass>>(); 

Pero lo que es la forma correcta de hacer esto con matrices?

Respuesta

22

Parece un poco derrotista, pero problemas como este son precisamente la razón por la cual la mayoría de la gente evita mezclar matrices y genéricos. Debido a la forma en que se implementan los genéricos (type erasure), las matrices y los genéricos nunca funcionarán bien juntos.

dos soluciones:

  • palillo a la utilización de una colección (por ejemplo ArrayList<Class<? extends SuperClass>>), que funciona igual de bien como una matriz y también permite la expansión.
  • Ponga una anotación @SuppressWarnings("unchecked") en el código que crea la matriz junto con un comentario que justifique su uso.
2

El problema es que es ilegal crear una matriz de un tipo genérico. La única forma de evitarlo es recurriendo al tipo genérico cuando crea la matriz, pero esa no es una solución muy buena. (Tenga en cuenta que es posible uso una matriz genérica, simplemente no crear uno:. Ver this question)

Usted debe ser casi siempre el uso de listas en lugar de matrices de todos modos, así que creo que se le ocurrió la mejor solución ya .

14

Utilice esta sintaxis:

Class<? extends SuperClass>[] avail = new Class[] { SubClass1.class, ... }; 

que le dará una advertencia "sin control", y con razón, ya que podría estar entre ellos un objeto Class para un tipo que no se extiende SuperClass en la matriz.

11

La forma correcta de hacer esto con matrices es hacerlo con una Colección. ¡Lo siento! Por un complicado conjunto de razones, las matrices no funcionan bien con los genéricos. Las matrices tienen un modelo de covarianza diferente al de los objetos genéricos, lo que finalmente causa los problemas con los que se está ejecutando. Por ejemplo, con matrices, pero no (normalmente) con los objetos genéricos, se puede hacer legalmente esto:

Object[] myArray = new String[5]; 

mientras que usted no puede hacer esto:

LinkedList<Object> myCollection = new LinkedList<String>(); 

Si usted desea más detalles, se puede ver Arrays In Java Generics la página de la excelente Generics FAQ

como simonn dicho esto, usted también puede utilizar las matrices como son, y utilizar @SuppressWarnings("unchecked") para silenciar las advertencias. Esto funcionará, pero sin el tipo de seguridad que los genéricos pueden proporcionarle. Si le preocupa el rendimiento, simplemente use un ArrayList, por lo que solo está usando un envoltorio delgado alrededor de una matriz, pero con todas las garantías de seguridad tipo provistas por los genéricos.

+1

covarianza! ¡Dijiste la mala palabra! –

4

But what's the right way to do this with arrays?

No hay una forma segura para hacer esto; usando la colección es el enfoque correcto. Para ver por qué, imagina si esto estuviera permitido. Usted podría tener una situación como esta:

// Illegal! 
Object[] baskets = new FruitBasket<? extends Citrus>[10]; 

// This is okay. 
baskets[0] = new FruitBasket<Lemon>(); 

// Danger! This should fail, but the type system will let it through. 
baskets[0] = new FruitBasket<Potato>(); 

El sistema de tipos necesita para detectar si una cesta que se agrega a la matriz es del tipo FruitBasket<? extends Citrus> o un subtipo. Un FruitBasket no coincide y debe rechazarse con un ArrayStoreException. ¡Pero nada pasa!

Debido a la supresión de tipo, la JVM solo puede ver el tipo de tiempo de ejecución de la matriz. En tiempo de ejecución, necesitamos comparar el tipo de matriz con el tipo de elemento para asegurarnos de que coincidan. El tipo de componente de tiempo de ejecución de la matriz es FruitBasket[] después de borrado de tipo; Del mismo modo, el tipo de tiempo de ejecución del elemento es FruitBasket. No se detectarán problemas, y es por eso que esto es peligroso.

0

No escriba seguro y con la advertencia de molde sin control:

Class<? extends SuperClass>[] availableTypes = 
    (Class<? extends SuperClass>[]) 
    (new Class[]{ SubClass1.class, SubClass2.class }); 
Cuestiones relacionadas