Por que o Java Generics não suporta tipos primitivos?

236

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>();
sgokhales
fonte
1
int i = (int) novo Object (); compila muito bem embora.
Sachin Verma

Respostas:

243

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.

thecoop
fonte
8
@DanyalAytekin - Na verdade, os genéricos Java são não tratada como modelos C ++ em tudo ...
Stephen C
20
Por que o compilador Java também não pode encaixar o tipo primitivo antes de ser usado? Isso deve ser possível, certo?
vrwim
13
@vrwim - Pode ser possível. Mas seria apenas açúcar sintático. O verdadeiro problema é que os genéricos Java com primitivas in a box são relativamente caros no tempo e no espaço em comparação com o modelo C ++ / C # ... onde o tipo primitivo real é usado.
Stephen C
6
@MauganRa sim, eu sei que posso :) Eu mantenho meu ponto de vista de que este é um projeto terrível. Espero que seja corrigido no java 10 (ou pelo menos ouvi dizer) e também em funções de ordem superior. Não me cite sobre isso.
Ced
4
@Ced concordo plenamente em que é design ruim que interminavelmente dói iniciantes ANO profissionais iguais
MauganRa
37

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.

Stephen C
fonte
11

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.

Piyush Sagar
fonte
Mais 1 para a nomenclatura adequada.
Drazen Bjelovuk 25/02
7

As coleções são definidas para exigir um tipo derivado de java.lang.Object. Os tipos básicos simplesmente não fazem isso.

ZeissS
fonte
26
Eu acho que a pergunta aqui é "por que". Por que os genéricos exigem objetos? O consenso parece ser que é menos uma opção de design e mais ceder à compatibilidade com versões anteriores. A meu ver, se os genéricos não conseguem lidar com primitivos, isso é um déficit de funcionalidade. Tal como está, tudo o que envolve primitivas deve ser escrito para cada primitiva: em vez de Comparator <t, t>, temos Integer.compare (int a, int b), Byte.compare (byte a, byte b), etc. Isso não é uma solução!
John P
1
Sim, genéricos sobre tipos primitivos seriam um recurso obrigatório. Aqui está um link para uma proposta para ele openjdk.java.net/jeps/218
crow
5

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.

vinS
fonte
0

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.

Atif
fonte