¿Cuál es la razón por la cual Java no nos permite hacer
private T[] elements = new T[initialCapacity];
Podría entender que .NET no nos permitió hacer eso, ya que en .NET tiene tipos de valor que en tiempo de ejecución pueden tener diferentes tamaños, pero en Java todo tipo de T serán referencias de objeto, por lo tanto tienen el mismo tamaño ( corrígeme si me equivoco).
¿Cuál es la razón?
java
generics
type-erasure
elysium devorado
fuente
fuente
Respuestas:
Se debe a que las matrices de Java (a diferencia de los genéricos) contienen, en tiempo de ejecución, información sobre su tipo de componente. Por lo tanto, debe conocer el tipo de componente cuando crea la matriz. Como no sabe qué
T
hay en tiempo de ejecución, no puede crear la matriz.fuente
ArrayList <SomeType>
lo hace entonces?new ArrayList<SomeType>()
? Los tipos genéricos no contienen el parámetro de tipo en tiempo de ejecución. El parámetro tipo no se usa en la creación. No hay diferencia en el código generado pornew ArrayList<SomeType>()
onew ArrayList<String>()
, onew ArrayList()
en absoluto.ArrayList<T>
funciona con su 'private T[] myArray
. En algún lugar del código, debe tener una matriz de tipo genérico T, entonces, ¿cómo?T[]
. Tiene una matriz de tipo de tiempo de ejecuciónObject[]
, y 1) el código fuente contiene una variable deObject[]
(así es en la última fuente de Oracle Java); o 2) el código fuente contiene una variable de tipoT[]
, lo cual es una mentira, pero no causa problemas debido a queT
se borra dentro del alcance de la clase.Citar:
(Creo que es Neal Gafter , pero no estoy seguro)
Véalo en contexto aquí: http://forums.sun.com/thread.jspa?threadID=457033&forumID=316
fuente
new T()
. Cada matriz en Java, por diseño, almacena el tipo de componente (es decirT.class
) dentro de ella; por lo tanto, necesita la clase de T en tiempo de ejecución para crear dicha matriz.new Box<?>[n]
, lo que a veces puede ser suficiente, aunque no ayudaría en su ejemplo.Box<String>[] bsa = new Box<String>[3];
¿supongo que algo cambió en java-8 en adelante?Al no proporcionar una solución decente, terminas con algo peor en mi humilde opinión.
La solución común es la siguiente.
se reemplaza con (suponiendo que T extiende Object y no otra clase)
Prefiero el primer ejemplo, sin embargo, más tipos académicos parecen preferir el segundo, o simplemente prefieren no pensar en ello.
La mayoría de los ejemplos de por qué no puede usar un Object [] se aplican igualmente a List o Collection (que son compatibles), por lo que los veo como argumentos muy pobres.
Nota: esta es una de las razones por las que la biblioteca de Colecciones en sí no se compila sin advertencias. Si este caso de uso no puede ser soportado sin advertencias, algo se rompe fundamentalmente con el modelo genérico en mi humilde opinión.
fuente
String[]
(o si la almacena en un campo de tipo de acceso públicoT[]
y alguien la recupera), obtendrá una ClassCastException.T[] ts = (T[]) new Object[n];
es una mala idea: stackoverflow.com/questions/21577493/…T[] ts = new T[n];
era un ejemplo válido. Mantendré el voto porque su respuesta puede causar problemas y confusiones a otros desarrolladores y también está fuera de tema. Además, dejaré de comentar sobre esto.Las matrices son covariantes
Esta última línea se compilaría bien, pero si ejecutamos este código, obtendríamos un
ArrayStoreException
porque estamos tratando de poner un doble en una matriz entera. El hecho de que estemos accediendo a la matriz a través de una referencia de Número es irrelevante aquí, lo que importa es que la matriz es una matriz de enteros.Esto significa que podemos engañar al compilador, pero no podemos engañar al sistema de tipo de tiempo de ejecución. Y esto es así porque las matrices son lo que llamamos un tipo reificable. Esto significa que en tiempo de ejecución Java sabe que esta matriz en realidad se instancia como una matriz de enteros a los que simplemente se accede a través de una referencia de tipo
Number[]
.Entonces, como podemos ver, una cosa es el tipo real del objeto, otra cosa es el tipo de referencia que usamos para acceder a él, ¿verdad?
El problema con los genéricos de Java
Ahora, el problema con los tipos genéricos en Java es que la información de tipo para los parámetros de tipo es descartada por el compilador después de la compilación del código; por lo tanto, este tipo de información no está disponible en tiempo de ejecución. Este proceso se llama borrado de tipo . Hay buenas razones para implementar genéricos como este en Java, pero esa es una larga historia, y tiene que ver con la compatibilidad binaria con el código preexistente.
Consideremos ahora el siguiente código inseguro:
Si el compilador de Java no nos impide hacer esto, el sistema de tipo de tiempo de ejecución tampoco puede detenernos, porque no hay forma, en tiempo de ejecución, de determinar que esta lista se suponía que era solo una lista de enteros. El tiempo de ejecución de Java nos permitiría incluir lo que queramos en esta lista, cuando solo debería contener enteros, porque cuando se creó, se declaró como una lista de enteros. Es por eso que el compilador rechaza la línea número 4 porque no es segura y, si se permite, podría romper los supuestos del sistema de tipos.
Como tal, los diseñadores de Java se aseguraron de que no podemos engañar al compilador. Si no podemos engañar al compilador (como podemos hacer con las matrices), tampoco podemos engañar al sistema de tipo de tiempo de ejecución.
Como tal, decimos que los tipos genéricos no son reificables, ya que en tiempo de ejecución no podemos determinar la verdadera naturaleza del tipo genérico.
Me salteé algunas partes de estas respuestas, puede leer el artículo completo aquí: https://dzone.com/articles/covariance-and-contravariance
fuente
La razón por la que esto es imposible es que Java implementa sus Genéricos únicamente en el nivel del compilador, y solo se genera un archivo de clase para cada clase. Esto se llama borrado de tipo .
En tiempo de ejecución, la clase compilada necesita manejar todos sus usos con el mismo código de bytes. Por
new T[capacity]
lo tanto, no tendría ni idea de qué tipo necesita ser instanciado.fuente
La respuesta ya se dio, pero si ya tiene una Instancia de T, puede hacer esto:
Espero poder ayudar, Ferdi265
fuente
T[] ts = t.clone(); for (int i=0; i<ts.length; i++) ts[i] = null;
.T[] t
, entonces lo sería(T[]) Array.newInstance(t.getClass().getComponentType(), length);
. Pasé algunas veces para averiguargetComponentType()
. Espero que esto ayude a otros.t.clone()
no volveráT[]
. Porquet
no es Array en esta respuesta.La razón principal se debe al hecho de que las matrices en Java son covariantes.
Hay una buena descripción general aquí .
fuente
String[]
aObject
y almacenar unaInteger
en el mismo. Entonces, tuvieron que agregar una verificación de tipo de tiempo de ejecución para los almacenes de matrices (ArrayStoreException
) porque el problema no se pudo detectar en tiempo de compilación. (De lo contrario, un enInteger
realidad podría estar atascado en unString[]
, y obtendría un error cuando intentara recuperarlo, lo que sería horrible.) ...Object
debería haber estadoObject[]
en mi primer comentario.Me gusta la respuesta indirectamente dada por Gafter . Sin embargo, propongo que esté mal. Cambié un poco el código de Gafter. Se compila y funciona durante un tiempo, luego bombardea donde Gafter predijo que lo haría
La salida es
Entonces, me parece que puede crear tipos de matriz genéricos en Java. ¿Entendí mal la pregunta?
fuente
Sé que llego un poco tarde a la fiesta aquí, pero pensé que podría ayudar a los futuros googlers ya que ninguna de estas respuestas solucionó mi problema. Sin embargo, la respuesta de Ferdi265 ayudó inmensamente.
Estoy tratando de crear mi propia lista de Linked, por lo que el siguiente código es lo que funcionó para mí:
En el método toArray () se encuentra la forma de crear una matriz de un tipo genérico para mí:
fuente
En mi caso, simplemente quería una serie de pilas, algo como esto:
Como esto no era posible, utilicé lo siguiente como solución alternativa:
Feo, pero Java es feliz.
Nota: como mencionó BrainSlugs83 en el comentario a la pregunta, es totalmente posible tener matrices de genéricos en .NET
fuente
Del tutorial de Oracle :
Para mí, suena muy débil. Creo que cualquiera con una comprensión suficiente de los genéricos, estaría perfectamente bien, e incluso esperaría, que ArrayStoredException no se arroje en tal caso.
fuente
Seguramente debe haber una buena forma de evitarlo (tal vez usando la reflexión), porque me parece que eso es exactamente lo que
ArrayList.toArray(T[] a)
hace. Yo cito:Entonces, una forma de evitarlo sería usar esta función, es decir, crear uno
ArrayList
de los objetos que desea en la matriz, luego usartoArray(T[] a)
para crear la matriz real. No sería rápido, pero no mencionaste tus requisitos.Entonces, ¿alguien sabe cómo
toArray(T[] a)
se implementa?fuente
Array.newInstance()
. Encontrará eso mencionado en muchas preguntas que preguntan cómo crear una matriz con un tipo desconocido en el momento de la compilación. Pero el OP preguntaba específicamente por qué no se puede usar lanew T[]
sintaxis, que es una pregunta diferenteEsto se debe a que los genéricos se agregaron a Java después de que lo hicieron, por lo que es un poco torpe porque los fabricantes originales de Java pensaron que al hacer una matriz, el tipo se especificaría en su fabricación. Entonces, eso no funciona con los genéricos, por lo que debe hacer E [] array = (E []) new Object [15]; Esto compila pero da una advertencia.
fuente
Si no podemos crear instancias de matrices genéricas, ¿por qué el lenguaje tiene tipos de matrices genéricas? ¿Cuál es el punto de tener un tipo sin objetos?
La única razón por la que puedo pensar es varargs -
foo(T...)
. De lo contrario, podrían haber eliminado completamente los tipos de matriz genéricos. (Bueno, en realidad no tenían que usar array para varargs, ya que los varargs no existían antes de 1.5 . Ese es probablemente otro error).Es mentira, ¡ puedes crear instancias de matrices genéricas a través de varargs!
Por supuesto, los problemas con las matrices genéricas siguen siendo reales, p. Ej.
Podemos usar este ejemplo para demostrar realmente el peligro de una matriz genérica .
Por otro lado, hemos estado usando varargs genéricos durante una década, y el cielo aún no se está cayendo. Entonces podemos argumentar que los problemas están siendo exagerados; no es un gran problema. Si se permite la creación explícita de matrices genéricas, tendremos errores aquí y allá; pero estamos acostumbrados a los problemas de borrado, y podemos vivir con eso.
Y podemos apuntar a
foo2
refutar la afirmación de que la especificación nos mantiene alejados de los problemas de los que dicen alejarnos. Si Sun tuviera más tiempo y recursos para 1.5 , creo que podrían haber alcanzado una resolución más satisfactoria.fuente
Como otros ya mencionaron, por supuesto puedes crear a través de algunos trucos .
Pero no es recomendable.
Debido a que el borrado de tipo y, lo que es más importante, la
covariance
matriz in que solo permite una matriz de subtipo se puede asignar a una matriz de supertipo, lo que le obliga a utilizar la conversión de tipo explícito al intentar recuperar el valor causando tiempo de ejecución,ClassCastException
que es uno de los objetivos principales que los genéricos intentan eliminar: verificaciones de tipo más fuertes en tiempo de compilación .Un ejemplo más directo se puede encontrar en Effective Java: Item 25 .
covarianza : una matriz de tipo S [] es un subtipo de T [] si S es un subtipo de T
fuente
Si la clase se usa como un tipo parametrizado, puede declarar una matriz de tipo T [], pero no puede instanciar directamente dicha matriz. En cambio, un enfoque común es crear una instancia de una matriz de tipo Object [], y luego hacer una conversión estrecha al tipo T [], como se muestra a continuación:
fuente
Prueba esto:
fuente