Por que os genéricos em Java funcionam com classes, mas não com tipos primitivos?
Por exemplo, isso funciona bem:
List<Integer> foo = new ArrayList<Integer>();
mas isso não é permitido:
List<int> bar = new ArrayList<int>();
Por que os genéricos em Java funcionam com classes, mas não com tipos primitivos?
Por exemplo, isso funciona bem:
List<Integer> foo = new ArrayList<Integer>();
mas isso não é permitido:
List<int> bar = new ArrayList<int>();
Respostas:
Os genéricos em Java são uma construção inteiramente em tempo de compilação - o compilador transforma todos os usos genéricos em transmissões para o tipo certo. Isso é para manter a compatibilidade com versões anteriores dos tempos de execução da JVM.
Este:
List<ClassA> list = new ArrayList<ClassA>();
list.add(new ClassA());
ClassA a = list.get(0);
é transformado em (aproximadamente):
List list = new ArrayList();
list.add(new ClassA());
ClassA a = (ClassA)list.get(0);
Portanto, qualquer coisa usada como genérica deve ser convertida em Object (neste exemplo, get(0)
retorna um Object
), e os tipos primitivos não. Portanto, eles não podem ser usados em genéricos.
Em Java, os genéricos funcionam da maneira que funcionam ... pelo menos em parte ... porque foram adicionados à linguagem alguns anos após o design da linguagem 1 . Os designers de linguagem foram restringidos em suas opções de genéricos, tendo que criar um design que fosse compatível com a linguagem existente e a biblioteca de classes Java .
Outras linguagens de programação (por exemplo, C ++, C #, Ada) permitem que tipos primitivos sejam usados como tipos de parâmetro para genéricos. Mas o outro lado disso é que as implementações de genéricos (ou tipos de modelo) dessas linguagens geralmente envolvem a geração de uma cópia distinta do tipo genérico para cada parametrização de tipo.
1 - A razão pela qual os genéricos não foram incluídos no Java 1.0 foi devido à pressão do tempo. Eles achavam que tinham que liberar a linguagem Java rapidamente para preencher a nova oportunidade de mercado apresentada pelos navegadores da web. James Gosling afirmou que gostaria de incluir genéricos se eles tivessem tempo. Como seria a linguagem Java se isso tivesse acontecido, é uma incógnita.
Em java, os genéricos são implementados usando "Type apagamento" para compatibilidade com versões anteriores. Todos os tipos genéricos são convertidos em Object no tempo de execução. por exemplo,
public class Container<T> {
private T data;
public T getData() {
return data;
}
}
será visto em tempo de execução como,
public class Container {
private Object data;
public Object getData() {
return data;
}
}
O compilador é responsável por fornecer o elenco adequado para garantir a segurança do tipo.
Container<Integer> val = new Container<Integer>();
Integer data = val.getData()
se tornará
Container val = new Container();
Integer data = (Integer) val.getData()
Agora, a pergunta é por que "Objeto" é escolhido como tipo em tempo de execução?
Resposta é Objeto é uma superclasse de todos os objetos e pode representar qualquer objeto definido pelo usuário.
Como todas as primitivas não herdam de " Object ", não podemos usá-la como um tipo genérico.
Para sua informação: O Projeto Valhalla está tentando resolver o problema acima.
As coleções são definidas para exigir um tipo derivado de java.lang.Object
. Os tipos básicos simplesmente não fazem isso.
De acordo com a Documentação Java , as variáveis de tipo genérico podem ser instanciadas apenas com tipos de referência, e não com tipos primitivos.
Isso deveria acontecer no Java 10 no Projeto Valhalla .
No artigo de Brian Goetz sobre o estado da especialização
Há uma excelente explicação sobre o motivo pelo qual os genéricos não eram suportados para os primitivos. E como será implementado em versões futuras do Java.
A atual implementação apagada de Java que produz uma classe para todas as instanciações de referência e sem suporte para instanciações primitivas. (Essa é uma tradução homogênea, e a restrição de que os genéricos de Java podem apenas atingir tipos de referência vem das limitações da tradução homogênea com relação ao conjunto de bytecodes da JVM, que usa bytecodes diferentes para operações em tipos de referência e tipos primitivos.) No entanto, os genéricos apagados em Java fornecem parametridade comportamental (métodos genéricos) e parametridade de dados (instanciações brutas e curinga de tipos genéricos).
...
foi escolhida uma estratégia de tradução homogênea, na qual variáveis genéricas do tipo são apagadas aos limites à medida que são incorporadas ao bytecode. Isso significa que, independentemente de uma classe ser genérica ou não, ela ainda é compilada em uma única classe, com o mesmo nome e cujas assinaturas de membros são iguais. A segurança do tipo é verificada em tempo de compilação e o tempo de execução é irrestrito pelo sistema de tipos genéricos. Por sua vez, isso impôs a restrição de que os genéricos só poderiam funcionar com tipos de referência, já que Object é o tipo mais geral disponível e não se estende aos tipos primitivos.
Ao criar um objeto, você não pode substituir um tipo primitivo pelo parâmetro type. Quanto ao motivo dessa restrição, é um problema de implementação do compilador. Os tipos primitivos têm suas próprias instruções de bytecode para carregar e armazenar na pilha da máquina virtual. Portanto, não é impossível compilar genéricos primitivos nesses caminhos separados de bytecode, mas isso complicaria o compilador.