¿Cómo puedo crear una matriz genérica en Java?

Resuelto tatsuhirosatou asked hace 15 años • 32 respuestas

Debido a la implementación de genéricos de Java, no puedes tener un código como este:

public class GenSet<E> {
    private E a[];

    public GenSet() {
        a = new E[INITIAL_ARRAY_LENGTH]; // Error: generic array creation
    }
}

¿Cómo puedo implementar esto manteniendo la seguridad de tipos?

Vi una solución en los foros de Java que dice así:

import java.lang.reflect.Array;

class Stack<T> {
    public Stack(Class<T> clazz, int capacity) {
        array = (T[])Array.newInstance(clazz, capacity);
    }

    private final T[] array;
}

¿Qué está sucediendo?

tatsuhirosatou avatar Feb 10 '09 00:02 tatsuhirosatou
Aceptado

A cambio, tengo que hacer una pregunta: ¿está GenSet"marcado" o "no marcado"? ¿Qué significa eso?

  • Comprobado : escritura fuerte . GenSetsabe explícitamente qué tipo de objetos contiene (es decir, su constructor fue llamado explícitamente con un Class<E>argumento, y los métodos generarán una excepción cuando se les pasen argumentos que no sean de tipo E. Consulte Collections.checkedCollection.

    -> en ese caso, deberías escribir:

    public class GenSet<E> {
    
        private E[] a;
    
        public GenSet(Class<E> c, int s) {
            // Use Array native method to create array
            // of a type only known at run time
            @SuppressWarnings("unchecked")
            final E[] a = (E[]) Array.newInstance(c, s);
            this.a = a;
        }
    
        E get(int i) {
            return a[i];
        }
    }
    
  • Sin marcar : escritura débil . En realidad, no se realiza ninguna verificación de tipos en ninguno de los objetos pasados ​​como argumento.

    -> en ese caso, deberías escribir

    public class GenSet<E> {
    
        private Object[] a;
    
        public GenSet(int s) {
            a = new Object[s];
        }
    
        E get(int i) {
            @SuppressWarnings("unchecked")
            final E e = (E) a[i];
            return e;
        }
    }
    

    Tenga en cuenta que el tipo de componente de la matriz debe ser el borrado del parámetro de tipo:

    public class GenSet<E extends Foo> { // E has an upper bound of Foo
    
        private Foo[] a; // E erases to Foo, so use Foo[]
    
        public GenSet(int s) {
            a = new Foo[s];
        }
    
        ...
    }
    

Todo esto es el resultado de una debilidad conocida y deliberada de los genéricos en Java: se implementaron mediante borrado, por lo que las clases "genéricas" no saben con qué tipo de argumento fueron creadas en tiempo de ejecución y, por lo tanto, no pueden proporcionar tipos de argumento. seguridad a menos que se implemente algún mecanismo explícito (verificación de tipo).

Varkhan avatar Feb 09 '2009 22:02 Varkhan

Puedes hacerlo:

E[] arr = (E[])new Object[INITIAL_ARRAY_LENGTH];

Esta es una de las formas sugeridas de implementar una colección genérica en Effective Java; Artículo 26 . Sin errores de tipo, no es necesario convertir la matriz repetidamente. Sin embargo , esto genera una advertencia porque es potencialmente peligroso y debe usarse con precaución. Como se detalla en los comentarios, esto Object[]ahora se hace pasar por nuestro E[]tipo y puede causar errores inesperados ClassCastExceptionsi se usa de manera insegura.

Como regla general, este comportamiento es seguro siempre que la matriz de conversión se use internamente (por ejemplo, para respaldar una estructura de datos) y no se devuelva ni se exponga al código del cliente. Si necesita devolver una matriz de tipo genérico a otro código, la Arrayclase de reflexión que menciona es el camino correcto a seguir.


Vale la pena mencionar que siempre que sea posible, será mucho más feliz trabajando con Lists en lugar de matrices si usa genéricos. Ciertamente, a veces no tienes otra opción, pero usar el marco de colecciones es mucho más sólido.

dimo414 avatar May 27 '2010 20:05 dimo414