Qual é o motivo pelo qual o Java não nos permite fazer
private T[] elements = new T[initialCapacity];
Eu pude entender que o .NET não nos permitiu fazer isso, pois no .NET você tem tipos de valor que em tempo de execução podem ter tamanhos diferentes, mas em Java todos os tipos de T serão referências a objetos, tendo o mesmo tamanho ( Corrija-me se eu estiver errado).
Qual é a razão?
java
generics
type-erasure
elísio devorado
fonte
fonte
Respostas:
Isso ocorre porque as matrizes de Java (ao contrário dos genéricos) contêm, em tempo de execução, informações sobre seu tipo de componente. Portanto, você deve conhecer o tipo de componente ao criar a matriz. Como você não sabe o que
T
está em tempo de execução, não é possível criar a matriz.fonte
ArrayList <SomeType>
isso então?new ArrayList<SomeType>()
? Tipos genéricos não contêm o parâmetro type em tempo de execução. O parâmetro type não é usado na criação. Não há diferença no código gerado pornew ArrayList<SomeType>()
ounew ArrayList<String>()
ou de formanew ArrayList()
alguma.ArrayList<T>
funciona com o seu 'private T[] myArray
. Em algum lugar do código, ele deve ter uma matriz do tipo T genérico, então como?T[]
. Ele possui uma matriz do tipo de tempo de execuçãoObject[]
e 1) o código fonte contém uma variável deObject[]
(é assim que está na fonte Java Oracle mais recente); ou 2) o código fonte contém uma variável do tipoT[]
, o que é uma mentira, mas não causa problemas devido àT
exclusão no escopo da classe.Citar:
(Eu acredito que é Neal Gafter , mas não tenho certeza)
Veja no contexto aqui: http://forums.sun.com/thread.jspa?threadID=457033&forumID=316
fonte
new T()
. Cada matriz em Java, por design, armazena o tipo de componente (ou sejaT.class
) dentro dela; portanto, você precisa da classe T em tempo de execução para criar essa matriz.new Box<?>[n]
, o que às vezes é suficiente, embora não ajude no seu exemplo.Box<String>[] bsa = new Box<String>[3];
alguma coisa mudou no java-8 e acima eu assumo?Ao não fornecer uma solução decente, você acaba com algo pior no IMHO.
O trabalho comum é o seguinte.
é substituído por (assumindo que T estende Object e não outra classe)
Eu prefiro o primeiro exemplo, no entanto, mais tipos acadêmicos parecem preferir o segundo, ou simplesmente preferem não pensar nisso.
A maioria dos exemplos de por que você não pode simplesmente usar um Object [] se aplica igualmente a List ou Collection (que são suportados), então eu os vejo como argumentos muito ruins.
Nota: esse é um dos motivos pelos quais a própria coleção Collections não compila sem avisos. Se esse caso de uso não puder ser suportado sem avisos, algo será fundamentalmente quebrado com o modelo de genéricos IMHO.
fonte
String[]
(ou se você a armazena em um campo publicamente acessível do tipoT[]
e alguém a recupera), elas receberão uma ClassCastException.T[] ts = (T[]) new Object[n];
é uma má idéia: stackoverflow.com/questions/21577493/...T[] ts = new T[n];
era um exemplo válido. Vou manter o voto, porque sua resposta pode causar problemas e confusões a outros desenvolvedores e também é fora de tópico. Além disso, vou parar de comentar sobre isso.Matrizes são covariantes
Essa última linha seria compilada muito bem, mas se executássemos esse código, obteríamos um
ArrayStoreException
porque estamos tentando colocar um duplo em uma matriz inteira. O fato de estarmos acessando a matriz por meio de uma referência Number é irrelevante aqui, o que importa é que a matriz é uma matriz de números inteiros.Isso significa que podemos enganar o compilador, mas não podemos enganar o sistema do tipo tempo de execução. E é assim porque matrizes são o que chamamos de tipo reificável. Isso significa que, em tempo de execução, Java sabe que essa matriz foi realmente instanciada como uma matriz de números inteiros que simplesmente é acessada por meio de uma referência do tipo
Number[]
.Então, como podemos ver, uma coisa é o tipo real do objeto, outra coisa é o tipo de referência que usamos para acessá-lo, certo?
O problema com Java Generics
Agora, o problema com tipos genéricos em Java é que as informações de tipo para os parâmetros de tipo são descartadas pelo compilador após a compilação do código; portanto, essas informações de tipo não estão disponíveis no tempo de execução. Esse processo é chamado de apagamento de tipo . Existem boas razões para implementar genéricos como este em Java, mas isso é uma longa história e tem a ver com compatibilidade binária com código pré-existente.
Vamos considerar agora o seguinte código não seguro:
Se o compilador Java não nos impedir de fazer isso, o sistema do tipo tempo de execução também não poderá nos impedir, porque não há como, em tempo de execução, determinar que essa lista deveria ser apenas uma lista de números inteiros. O tempo de execução do Java nos permite colocar o que queremos nesta lista, quando deve conter apenas números inteiros, porque quando foi criado, foi declarado como uma lista de números inteiros. É por isso que o compilador rejeita a linha número 4 porque é inseguro e, se permitido, pode quebrar as suposições do sistema de tipos.
Como tal, os designers de Java se certificaram de que não podemos enganar o compilador. Se não podemos enganar o compilador (como podemos fazer com matrizes), também não podemos enganar o sistema do tipo tempo de execução.
Como tal, dizemos que os tipos genéricos não são reificáveis, pois em tempo de execução não podemos determinar a verdadeira natureza do tipo genérico.
Eu pulei algumas partes destas respostas, você pode ler o artigo completo aqui: https://dzone.com/articles/covariance-and-contravariance
fonte
A razão pela qual isso é impossível é que o Java implementa seus Genéricos puramente no nível do compilador e há apenas um arquivo de classe gerado para cada classe. Isso é chamado de apagamento de tipo .
No tempo de execução, a classe compilada precisa lidar com todos os seus usos com o mesmo bytecode. Portanto,
new T[capacity]
não teria absolutamente nenhuma idéia de que tipo precisa ser instanciado.fonte
A resposta já foi dada, mas se você já tiver uma Instância de T, poderá fazer o seguinte:
Espero poder ajudar, Ferdi265
fonte
T[] ts = t.clone(); for (int i=0; i<ts.length; i++) ts[i] = null;
.T[] t
, seria(T[]) Array.newInstance(t.getClass().getComponentType(), length);
. Passei algumas vezes para descobrirgetComponentType()
. Espero que isso ajude os outros.t.clone()
não retornaráT[]
. Porquet
não é a matriz nesta resposta.O principal motivo é que matrizes em Java são covariantes.
Há uma boa visão geral aqui .
fonte
String[]
paraObject
e armazenar umInteger
nele. Portanto, eles tiveram que adicionar uma verificação de tipo de tempo de execução para armazenamentos de matriz (ArrayStoreException
) porque o problema não pôde ser detectado no momento da compilação. (Caso contrário, umInteger
realmente poderia estar preso em umString[]
e você receberia um erro ao tentar recuperá-lo, o que seria horrível.) ...Object
deveria estarObject[]
no meu primeiro comentário.Eu gosto da resposta indiretamente dada por Gafter . No entanto, proponho que está errado. Mudei um pouco o código de Gafter. Ele compila e funciona por um tempo e depois bombardeia onde Gafter previu que seria
A saída é
Então, parece-me que você pode criar tipos de matriz genéricos em java. Eu entendi mal a pergunta?
fonte
Sei que estou um pouco atrasado para a festa aqui, mas achei que poderia ajudar futuros googlers, pois nenhuma dessas respostas resolveu meu problema. A resposta de Ferdi265 ajudou imensamente.
Estou tentando criar minha própria lista vinculada, portanto, o seguinte código foi o que funcionou para mim:
No método toArray (), está o caminho para criar uma matriz de um tipo genérico para mim:
fonte
No meu caso, eu simplesmente queria uma variedade de pilhas, algo como isto:
Como isso não foi possível, usei o seguinte como solução alternativa:
Feio, mas Java é feliz.
Nota: conforme mencionado por BrainSlugs83 no comentário à pergunta, é totalmente possível ter matrizes de genéricos no .NET
fonte
No tutorial do Oracle :
Para mim, parece muito fraco. Acho que qualquer pessoa com um entendimento suficiente de genéricos estaria perfeitamente bem, e até esperaria, que a ArrayStoredException não seja lançada nesse caso.
fonte
Certamente deve haver uma boa maneira de contornar isso (talvez usando reflexão), porque me parece que é exatamente
ArrayList.toArray(T[] a)
isso que faz. Eu cito:Portanto, uma maneira de contornar seria usar essa função, ou seja, criar um
ArrayList
dos objetos que você deseja na matriz e, em seguida, usartoArray(T[] a)
para criar a matriz real. Não seria rápido, mas você não mencionou seus requisitos.Então, alguém sabe como
toArray(T[] a)
é implementado?fonte
Array.newInstance()
. Você encontrará isso mencionado em muitas perguntas que perguntam como criar uma matriz com um tipo desconhecido em tempo de compilação. Mas o OP estava pedindo especificamente por que você não pode usar anew T[]
sintaxe, que é uma questão diferenteÉ porque os genéricos foram adicionados ao java depois que eles o criaram, então é meio desajeitado porque os criadores originais do java pensavam que, ao criar uma matriz, o tipo seria especificado na criação. Portanto, como isso não funciona com genéricos, é necessário fazer E [] array = (E []) new Object [15]; Isso compila, mas dá um aviso.
fonte
Se não podemos instanciar matrizes genéricas, por que o idioma possui tipos de matriz genéricos? Qual é o sentido de ter um tipo sem objetos?
A única razão pela qual consigo pensar é em varargs -
foo(T...)
. Caso contrário, eles poderiam ter eliminado completamente os tipos de matriz genérica. (Bem, eles realmente não precisaram usar array para varargs, pois os varargs não existiam antes da versão 1.5 . Isso provavelmente é outro erro.)Portanto, é uma mentira, você pode instanciar matrizes genéricas, através de varargs!
Obviamente, os problemas com matrizes genéricas ainda são reais, por exemplo,
Podemos usar este exemplo para realmente demonstrar o perigo da matriz genérica .
Por outro lado, usamos varargs genéricos há uma década e o céu ainda não está caindo. Então, podemos argumentar que os problemas estão sendo exagerados; não é grande coisa. Se a criação de matriz genérica explícita for permitida, teremos erros aqui e ali; mas estamos acostumados com os problemas de apagamento e podemos conviver com isso.
E podemos apontar para
foo2
refutar a afirmação de que as especificações nos mantêm longe dos problemas que eles afirmam nos impedir. Se a Sun tivesse mais tempo e recursos para o 1.5 , acredito que eles poderiam ter alcançado uma resolução mais satisfatória.fonte
Como outros já mencionados, é claro que você pode criar através de alguns truques .
Mas não é recomendado.
Como a eliminação de tipo e, mais importante, a
covariance
matriz in, que apenas permite uma matriz de subtipo, pode ser atribuída a uma matriz de supertipo, o que força você a usar a conversão explícita de tipo ao tentar recuperar o valor, causando o tempo de execução,ClassCastException
que é um dos principais objetivos que os genéricos tentam eliminar: Verificações mais fortes em tempo de compilação .Um exemplo mais direto pode ser encontrado em Java Efetivo: Item 25 .
covariância : uma matriz do tipo S [] é um subtipo de T [] se S é um subtipo de T
fonte
Se a classe usar como um tipo parametrizado, poderá declarar uma matriz do tipo T [], mas não poderá instanciar diretamente essa matriz. Em vez disso, uma abordagem comum é instanciar uma matriz do tipo Object [] e, em seguida, fazer uma conversão estreita para o tipo T [], conforme mostrado a seguir:
fonte
Tente o seguinte:
fonte