Arrays generics en Java

De acuerdo, he estado buscando en la web, y parece que no puedo encontrar ninguna solución a mi problema. Encontré muchas soluciones, pero no todas las adecuadas.

Necesito crear una variedad de generics. Pero el tipo genérico en sí mismo se extiende Comparable. Cuando bash lo siguiente:

public class Hash<T extends Comparable> { private T[] hashTable; private int tableSize; Hash(int records, double load) { tableSize = (int)(records / loadFactor); tableSize = findNextPrime(tableSize); hashTable = (T[])(new Object[tableSize]); //Error: Ljava.lang.Object; cannot be cast to [Ljava.lang.Comparable; } } 

El problema es que el Objeto no puede ser lanzado como un genérico que se extiende Comparable. ¿Hay alguna forma de evitar esto?

Los generics y las matrices no se mezclan, básicamente. La respuesta corta es que puede evitar este problema. La respuesta más larga es que probablemente no deberías y explicaré por qué.

Puede usar Array.newInstance() esta manera:

 private Comparable[] hashtable; ... hashtable = (Comparable[])Array.newInstance(Comparable.class, tableSize); 

pero no puedes crear una matriz de tu tipo parametrizado.

Las matrices son covariantes . Eso significa que conservan el tipo de sus elementos en tiempo de ejecución. Los generics de Java no lo son. Usan borrado de tipo para enmascarar básicamente el lanzamiento implícito que está sucediendo. Es importante entender eso.

Por lo tanto, cuando crea una matriz de objetos no puede convertirla a, por ejemplo, una matriz comparable (o de cualquier otro tipo) porque eso no es correcto.

Para darle un ejemplo. Con los generics esto es perfectamente legal:

 List list = new ArrayList(); List list2 = (List)list; list.add(3); 

También es por eso que no puedes hacer esto:

 public  T newInstance(T t) { return new T(); // error! } 

es decir, en tiempo de ejecución no hay conocimiento de la clase de T. Esta es la razón por la cual el código anterior se escribe más a menudo como:

 public  T newInstance(T t, Class clazz) { return clazz.newInstance(); } 

porque no es un tipo de tiempo de ejecución para el argumento genérico. Pero con matrices:

 String arr[] = new String[10]; Integer arr2[] = (Integer[])arr; // error! 

Lo que debería hacer en este caso (imho) no es usar matrices sino utilizar una ArrayList . Honestamente, hay muy pocas razones para usar arrays sobre una ArrayList y los generics son solo un ejemplo de eso.

Para una explicación mejor y más completa, vea las (excelentes) Preguntas frecuentes sobre Java Generics :

¿Puedo crear una matriz cuyo tipo de componente es un tipo concreto parametrizado?

No, porque no es seguro para tipos.

Las matrices son covariantes, lo que significa que una matriz de referencias de supertipo es un supertipo de una matriz de referencias de subtipo. Es decir, Object[] es un supertipo de String[] y se puede acceder a una matriz de cadenas a través de una variable de referencia de tipo Object[] .

Las otras respuestas aquí en general, abogan por un mejor enfoque para esto (especialmente la recomendación de utilizar una ArrayList en su lugar), pero una respuesta simple en este caso específico podría ser hacer:

 hashTable = (T[])(new Comparable[tableSize]); 

(es decir, crea una matriz de tipo raw Comparable en lugar de Object)

Si encapsula correctamente todo el acceso a esta matriz dentro de su objeto Hash, esto debería funcionar, pero (como explican las demás respuestas) podría dejarse vulnerable.

El reparto que estás intentando

 (T[])(new Object[tableSize]); 

falla, porque los elementos en la matriz son instancias de Objeto. El objeto no extiende Comparable , por lo que el molde (T []) falla porque T se define como:

 T extends Comparable 

Para resolver este problema, ya sea:

  • Crea una instancia de la matriz para que sus elementos sean instancias de alguna clase que extienda Comparable
  • Cambiar hashTable de una matriz (que no es un tipo genérico), a un tipo de colección genérica, por ejemplo, List hashTable = new ArrayList(tableSize>)

A menudo se encuentra con problemas cuando necesita instanciar algo de un tipo genérico. La forma más fácil de evitar esto es pasar la clase de realidad que se almacenará en el constructor. De esta forma puedes construir desde el tipo real. Pruebe algo como esto:

 public class Hash> { Hash(int records, double load, Class class) { tableSize = (int)(records / loadFactor); tableSize = findNextPrime(tableSize); hashTable = java.lang.reflect.Array.newInstance(class, tableSize); } private T[] hashTable; private int tableSize; } 

El lanzamiento forzado sugerido por otras personas no funcionó para mí, lanzando una excepción de casting ilegal.

Sin embargo, este elenco implícito funcionó bien:

 Item[] array = new Item[SIZE]; 

donde Item es una clase I definida que contiene el miembro:

 private K value; 

De esta forma, obtienes una matriz de tipo K (si el elemento solo tiene el valor) o cualquier tipo genérico que quieras definir en la clase Artículo.