Debido a la implementación de los genéricos de Java, no puede 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 tipo?
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;
}
Pero realmente no entiendo lo que está pasando.
java
arrays
generics
reflection
instantiation
tatsuhirosatou
fuente
fuente
Respuestas:
Tengo que hacer una pregunta a cambio: ¿está
GenSet
"marcado" o "no marcado"? Qué significa eso?Comprobado : mecanografía fuerte .
GenSet
sabe explícitamente qué tipo de objetos contiene (es decir, su constructor fue llamado explícitamente con unClass<E>
argumento, y los métodos arrojarán una excepción cuando se pasen argumentos que no son de tipoE
. VerCollections.checkedCollection
.-> en ese caso, deberías escribir:
Sin marcar : mecanografía débil . En realidad, no se realiza ninguna verificación de tipo en ninguno de los objetos pasados como argumento.
-> en ese caso, deberías escribir
Tenga en cuenta que el tipo de componente de la matriz debe ser la eliminación del parámetro de tipo:
Todo esto es el resultado de una debilidad conocida y deliberada de los genéricos en Java: se implementó usando el borrado, por lo que las clases "genéricas" no saben con qué argumento de tipo se crearon en tiempo de ejecución y, por lo tanto, no pueden proporcionar tipo- seguridad a menos que se implemente algún mecanismo explícito (verificación de tipo).
fuente
Object[] EMPTY_ELEMENTDATA = {}
para el almacenamiento. ¿Puedo usar este mecanismo para cambiar el tamaño sin conocer el tipo que usa genéricos?public void <T> T[] newArray(Class<T> type, int length) { ... }
Puedes hacerlo:
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 emitir la matriz repetidamente Sin embargo, esto desencadena una advertencia porque es potencialmente peligroso y debe usarse con precaución. Como se detalla en los comentarios, esto
Object[]
ahora se está haciendo pasar por nuestroE[]
tipo y puede causar errores inesperados oClassCastException
haciendo pasar s si 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 o se exponga al código del cliente. Si necesita devolver una matriz de un tipo genérico a otro código, la reflexión
Array
clase de que menciona es el camino correcto.Vale la pena mencionar que, siempre que sea posible, pasará un tiempo mucho más feliz trabajando con
List
s 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 robusto.fuente
String[] s=b;
en eltest()
método anterior . Eso es porque la matriz de E no es realmente, es Object []. Esto es importante si lo desea, por ejemplo, unList<String>[]
- no puede usar unObject[]
para eso, debe tener unList[]
específicamente. Es por eso que necesita usar la creación de matriz Class <?> Reflejada.public E[] toArray() { return (E[])internalArray.clone(); }
cuandointernalArray
se escribe comoE[]
y, por lo tanto, en realidad es unObject[]
. Esto falla en tiempo de ejecución con una excepción de conversión de tipo porqueObject[]
no se puede asignar a una matriz del tipoE
que sea.E[] b = (E[])new Object[1];
puede ver claramente que la única referencia a la matriz creada esb
y que el tipo deb
esE[]
. Por lo tanto, no hay peligro de que acceda accidentalmente a la misma matriz a través de una variable diferente de un tipo diferente. Si, en cambio, tuvieras que hacerlo, tendríasObject[] a = new Object[1]; E[]b = (E[])a;
que ser paranoico sobre cómo lo usasa
.Aquí le mostramos cómo usar los genéricos para obtener una matriz del tipo que está buscando y al mismo tiempo preservar la seguridad de la fuente (a diferencia de las otras respuestas, que le devolverán una
Object
matriz o generarán advertencias en el momento de la compilación):Eso se compila sin advertencias y, como puede ver
main
, para cualquier tipo de declaración de instanciaGenSet
, puede asignara
a una matriz de ese tipo, y puede asignar un elementoa
a una variable de ese tipo, lo que significa que la matriz y los valores en la matriz son del tipo correcto.Funciona mediante el uso de literales de clase como tokens de tipo de tiempo de ejecución, como se explica en los Tutoriales de Java . Los literales de clase son tratados por el compilador como instancias de
java.lang.Class
. Para usar uno, simplemente siga el nombre de una clase con.class
. Entonces,String.class
actúa como unClass
objeto que representa la claseString
. Esto también funciona para interfaces, enumeraciones, matrices de cualquier dimensión (pString[].class
. Ej. ), Primitivas (pint.class
. Ej. ) Y la palabra clavevoid
( pvoid.class
. Ej .).Class
en sí es genérico (declarado comoClass<T>
, dondeT
representa el tipo queClass
representa el objeto), lo que significa que el tipo deString.class
esClass<String>
.Entonces, cada vez que llama al constructor
GenSet
, pasa un literal de clase para el primer argumento que representa una matriz delGenSet
tipo declarado de la instancia (por ejemplo,String[].class
paraGenSet<String>
). Tenga en cuenta que no podrá obtener una matriz de primitivas, ya que las primitivas no se pueden usar para variables de tipo.Dentro del constructor, llamar al método
cast
devuelve elObject
argumento pasado emitido a la clase representada por elClass
objeto en el que se llamó el método. Llamar al método estáticonewInstance
enjava.lang.reflect.Array
devuelve como unaObject
matriz del tipo representado por elClass
objeto pasado como primer argumento y de la longitud especificada por elint
pasado como segundo argumento. Llamar al métodogetComponentType
devuelve unClass
objeto que representa el tipo de componente de la matriz representada por elClass
objeto en el que se llamó el método (por ejemplo ,String.class
paraString[].class
,null
si elClass
objeto no representa una matriz).Esa última oración no es del todo precisa. Llamar
String[].class.getComponentType()
devuelve unClass
objeto que representa la claseString
, pero su tipo esClass<?>
, noClass<String>
, por lo que no puede hacer algo como lo siguiente.Lo mismo ocurre con cada método
Class
que devuelve unClass
objeto.Con respecto al comentario de Joachim Sauer sobre esta respuesta (no tengo suficiente reputación para comentarlo yo mismo), el ejemplo que usa el elenco
T[]
dará como resultado una advertencia porque el compilador no puede garantizar la seguridad de tipo en ese caso.Editar con respecto a los comentarios de Ingo:
fuente
Esta es la única respuesta segura.
fuente
Arrays#copyOf()
es independiente de la longitud de la matriz suministrada como primer argumento. Eso es inteligente, aunque paga el costo de las llamadasMath#min()
ySystem#arrayCopy()
, ninguno de los cuales es estrictamente necesario para hacer este trabajo. docs.oracle.com/javase/7/docs/api/java/util/…E
es una variable de tipo. Los varargs crean una matriz de borrado deE
cuándoE
es una variable de tipo, por lo que no es muy diferente de(E[])new Object[n]
. Consulte http://ideone.com/T8xF91 . De ninguna manera es más seguro de tipo que cualquier otra respuesta.new E[]
. El problema que mostró en su ejemplo es un problema de borrado general, no exclusivo de esta pregunta y esta respuesta.Para extender a más dimensiones, sólo tiene que añadir
[]
parámetros de dimensión a 's ynewInstance()
(T
es un parámetro de tipo,cls
es unaClass<T>
,d1
a travésd5
son números enteros):Ver
Array.newInstance()
para más detalles.fuente
En Java 8, podemos hacer una especie de creación de matriz genérica utilizando una referencia lambda o método. Esto es similar al enfoque reflexivo (que pasa a
Class
), pero aquí no estamos usando la reflexión.Por ejemplo, esto es usado por
<A> A[] Stream.toArray(IntFunction<A[]>)
.Esto podría también hacerse pre-Java 8 utilizando las clases anónimas pero es más engorroso.
fuente
ArraySupplier
esta, puede declarar el constructor comoGenSet(Supplier<E[]> supplier) { ...
y llamarlo con la misma línea que tiene.IntFunction<E[]>
, pero sí, eso es cierto.Esto está cubierto en el Capítulo 5 (Genéricos) de Effective Java, 2nd Edition , item 25 ... Listas preferidas a las matrices
Su código funcionará, aunque generará una advertencia no verificada (que podría suprimir con la siguiente anotación:
Sin embargo, probablemente sería mejor usar una Lista en lugar de una Matriz.
Hay una discusión interesante sobre este error / característica en el sitio del proyecto OpenJDK .
fuente
No es necesario pasar el argumento de clase al constructor. Prueba esto.
y
resultado:
fuente
Los genéricos de Java funcionan verificando los tipos en el momento de la compilación e insertando las conversiones apropiadas, pero borrando los tipos en los archivos compilados. Esto hace que las bibliotecas genéricas sean utilizables por código que no comprende los genéricos (que fue una decisión de diseño deliberada) pero que significa que normalmente no puede averiguar cuál es el tipo en tiempo de ejecución.
El
Stack(Class<T> clazz,int capacity)
constructor público requiere que pase un objeto de clase en tiempo de ejecución, lo que significa que la información de clase está disponible en tiempo de ejecución para codificarla. Y elClass<T>
formulario significa que el compilador verificará que el objeto Class que pasa es precisamente el objeto Class para el tipo T. No es una subclase de T, no una superclase de T, sino precisamente T.Esto significa que puede crear un objeto de matriz del tipo apropiado en su constructor, lo que significa que el tipo de los objetos que almacena en su colección verificará sus tipos en el momento en que se agreguen a la colección.
fuente
Hola, aunque el hilo está muerto, me gustaría llamar su atención sobre esto:
Los genéricos se utilizan para la verificación de tipos durante el tiempo de compilación:
No se preocupe por las advertencias de tipografía cuando escriba una clase genérica. Preocúpate cuando lo estés usando.
fuente
¿Qué hay de esta solución?
Funciona y parece demasiado simple para ser verdad. ¿Hay algún inconveniente?
fuente
T[]
, entonces no puede construir programáticamente unT[] elems
para pasar a la función. Y si pudieras, no necesitarías la función.Mira también este código:
Convierte una lista de cualquier tipo de objeto en una matriz del mismo tipo.
fuente
List
tiene más de un tipo de objeto, por ejemplotoArray(Arrays.asList("abc", new Object()))
, lanzaráArrayStoreException
.for
bucle y otros que utilicéArrays.fill(res, obj);
ya que quería el mismo valor para cada índice.He encontrado una manera rápida y fácil que funciona para mí. Tenga en cuenta que solo he usado esto en Java JDK 8. No sé si funcionará con versiones anteriores.
Aunque no podemos instanciar una matriz genérica de un parámetro de tipo específico, podemos pasar una matriz ya creada a un constructor de clase genérico.
Ahora en main podemos crear la matriz así:
Para una mayor flexibilidad con sus matrices, puede usar una lista vinculada, por ejemplo. ArrayList y otros métodos encontrados en la clase Java.util.ArrayList.
fuente
El ejemplo está utilizando la reflexión de Java para crear una matriz. Hacer esto generalmente no se recomienda, ya que no es seguro para escribir. En cambio, lo que debe hacer es usar una Lista interna y evitar la matriz.
fuente
Pasando una lista de valores ...
fuente
Hice este fragmento de código para crear una instancia reflexiva de una clase que se pasa para una simple utilidad de prueba automatizada.
Tenga en cuenta este segmento:
para el inicio de la matriz donde Array.newInstance (clase de matriz, tamaño de matriz) . La clase puede ser primitiva (int.class) y object (Integer.class).
BeanUtils es parte de la primavera.
fuente
En realidad, una forma más fácil de hacerlo es crear una matriz de objetos y convertirlos al tipo deseado, como en el siguiente ejemplo:
donde
SIZE
es una constante yT
es un identificador de tipofuente
El lanzamiento forzado sugerido por otras personas no funcionó para mí, arrojando una excepción de lanzamiento ilegal.
Sin embargo, este elenco implícito funcionó bien:
donde Item es una clase que definí que contiene el miembro:
De esta forma, obtiene una matriz de tipo K (si el elemento solo tiene el valor) o cualquier tipo genérico que desee definir en la clase Elemento.
fuente
Nadie más ha respondido a la pregunta de lo que está sucediendo en el ejemplo que publicaste.
Como otros han dicho, los genéricos se "borran" durante la compilación. Entonces, en tiempo de ejecución, una instancia de un genérico no sabe cuál es su tipo de componente. La razón de esto es histórica, Sun quería agregar genéricos sin romper la interfaz existente (tanto fuente como binaria).
Las matrices, por otro lado , conocen su tipo de componente en tiempo de ejecución.
Este ejemplo soluciona el problema al hacer que el código que llama al constructor (que conoce el tipo) pase un parámetro que le dice a la clase el tipo requerido.
Entonces la aplicación construiría la clase con algo como
y el constructor ahora sabe (en tiempo de ejecución) cuál es el tipo de componente y puede usar esa información para construir la matriz a través de la API de reflexión.
Finalmente, tenemos un tipo de conversión porque el compilador no tiene forma de saber que la matriz devuelta
Array#newInstance()
es del tipo correcto (aunque lo sepamos).Este estilo es un poco feo, pero a veces puede ser la solución menos mala para crear tipos genéricos que necesitan conocer su tipo de componente en tiempo de ejecución por cualquier razón (creando matrices o instancias de su tipo de componente, etc.).
fuente
Encontré una especie de solución a este problema.
La siguiente línea arroja un error genérico de creación de matriz
Sin embargo, si encapsulo
List<Person>
en una clase separada, funciona.Puede exponer a las personas en la clase PersonList a través de un captador. La línea a continuación le dará una matriz, que tiene un
List<Person>
elemento en cada elemento. En otras palabras, matriz deList<Person>
.Necesitaba algo como esto en algún código en el que estaba trabajando y esto es lo que hice para que funcione. Hasta el momento no hay problemas.
fuente
Puede crear una matriz de Objetos y enviarla a E en todas partes. Sí, no es una forma muy limpia de hacerlo, pero al menos debería funcionar.
fuente
prueba esto.
fuente
Element
viene tu clase?Una solución fácil, aunque desordenada, para esto sería anidar una segunda clase "titular" dentro de su clase principal y usarla para guardar sus datos.
fuente
new Holder<Thing>[10]
es una creación de matriz genérica.Tal vez no esté relacionado con esta pregunta, pero mientras recibía el "
generic array creation
" error por usarDescubro los siguientes trabajos (y funcionó para mí) con
@SuppressWarnings({"unchecked"})
:fuente
Me pregunto si este código crearía una matriz genérica efectiva.
Editar: ¿Quizás una forma alternativa de crear una matriz de este tipo, si el tamaño que necesitabas fuera conocido y pequeño, sería simplemente alimentar el número requerido de "nulos" en el comando zeroArray?
Aunque obviamente esto no es tan versátil como usar el código createArray.
fuente
T
cuandoT
es una variable de tipo, es decir,zeroArray
devuelve unObject[]
. Ver http://ideone.com/T8xF91 .Podrías usar un yeso:
fuente
a
fuera de la clase!En realidad, encontré una solución bastante única para evitar la incapacidad de iniciar una matriz genérica. Lo que tiene que hacer es crear una clase que tome la variable genérica T así:
y luego en tu clase de matriz solo haz que comience así:
iniciar un
new Generic Invoker[]
causará un problema sin marcar, pero en realidad no debería haber ningún problema.Para obtener de la matriz, debe llamar a la matriz [i] .variable de esta manera:
El resto, como cambiar el tamaño de la matriz, se puede hacer con Arrays.copyOf () de esta manera:
Y la función de agregar se puede agregar así:
fuente
T
, no una matriz de algún tipo parametrizado.Según vnportnoy, la sintaxis
crea una matriz de referencias nulas, que se completarán como
Que es de tipo seguro.
fuente
fuente
La creación genérica de matrices no está permitida en Java, pero puede hacerlo como
fuente