Crear arrays genéricos en Java

JMH es un framework que utilizamos en nuestro día a día como investigadores y que ya hemos comentado aquí. JMH hace uso intensivo de los generics de Java, y esto facilita enormemente el desarrollo además de proporcionar una buena seguridad ante tipos.

Una de las clases que tenemos en JMH es Solution. Se trata de una clase abstracta que representa soluciones. Esta clase está pensada para ser subclasificada, es decir, implementar clases hijas de ella. JMH es un framework para implementar algoritmos que resuelven problemas de optimización, y cada problema representa las soluciones de una manera. Por tanto, cada problema tiene su propia clase hija de Solution.

Algunos algoritmos requieren mantener un array de soluciones. Por ejemplo, tenemos una clase EliteSet que internamente mantiene un array de soluciones. EliteSet es una clase generificada, de forma que se pueda utilizar con soluciones de cualquier tipo:


public class EliteSet<S extends Solution, I extends Instance> {
private S[] solutions;
...
public S[] getSolutions() {
return solutions;
}
}

Los algoritmos que hacen uso de objetos EliteSet, hacen cosas como esta:


EliteSet es = new EliteSet(...);
GOPSolution[] solutions = es.getSolutions();

Este código lanza ClassCastException. No es posible convertir el resultado devuelto por es.getSolutions() a GOPSolution[]. La razón hay que buscarla en cómo se crea el array dentro de la clase EliteSet:


public EliteSet(...) {
this.solutions = (S[]) new Solution[b];
}

Vaya, se crea como un array de Solution, no como un array de S. ¿Por qué no hacer esto?:


this.solutions = new S[b];

No podemos hacer esto último. En tiempo de ejecución, que es cuando vamos a crear el array, no tenemos información sobre los parámetros de tipo, esta información desaparece y sólo está presente en tiempo de compilación como un mecanismo más de seguridad ante errores de tipos (y es realmente útil). Por tanto, en tiempo de ejecución no podemos hacer referencia a S. Hay que crear un array de Solution, aunque vaya a contener clases hijas de Solution.

El engorro de esto es que hay que estar convirtiendo cada solución visitada del array a GOPSolution con un casting explícito. Podemos evitarnos este problema de la siguiente manera (implica cambiar la API de la clase EliteSet):


public EliteSet(Class clazz, ...) {
this.solutions = (S[]) Array.newInstance(clazz, b);
}

Ahora necesito pasar la clase al crear el objeto EliteSet:


EliteSet<GOPSolution, GOPInstance> es = new EliteSet<GOPSolution, GOPInstance>(GOPSolution.class, ...)

Puedes encontrar algunas discusiones interesantes sobre el tema en los foros de Sun/Oracle.

Anuncios

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s