Por que o Java não faz inferência de tipo?

44

Sempre me perguntei por que o Java não faz inferência de tipo, uma vez que a linguagem é o que é e sua VM é muito madura. O Google's Go é um exemplo de idioma com excelente inferência de tipo e reduz a quantidade de digitação necessária. Existe alguma razão especial por trás desse recurso não fazer parte do Java?

cobie
fonte
3
para trás regras de compatibilidade em uma língua tão antiga e popular como java
catraca aberração
9
@ratchetfreak A inferência de tipo não pode ser adicionada de uma maneira compatível com versões anteriores? Programas mais antigos apenas forneceriam mais informações de tipo do que o necessário.
1
Pode ter alguns efeitos indesejados quando usado com o apagamento de tipo. docs.oracle.com/javase/tutorial/java/generics/…
Zachary Yates
4
Observe que o Java 8 trará muita inferência de tipo com seu recurso Lambda: você pode escrever lambdas complexas sem mencionar nenhum tipo e tudo que está sendo inferido.
Joachim Sauer
4
A inferência de tipo de variável local está chegando ao Java: JEP 286: Inferência de tipo de variável local
Jimmy Page

Respostas:

60

Tecnicamente falando, Java possui inferências de tipo ao usar genéricos. Com um método genérico como

public <T> T foo(T t) {
  return t;
}

O compilador analisará e entenderá que quando você escreve

// String
foo("bar");
// Integer
foo(new Integer(42));

Uma String será retornada para a primeira chamada e um Inteiro para a segunda chamada com base no que foi inserido como argumento. Você obterá a verificação adequada em tempo de compilação como resultado. Além disso, no Java 7, é possível obter algumas inferências de tipo adicionais ao instanciar genéricos como esse

Map<String, String> foo = new HashMap<>();

Java tem a gentileza de preencher os colchetes angulares em branco para nós. Agora, por que o Java não suporta inferências de tipo como parte da atribuição de variáveis? Em um ponto, havia uma RFE para inferir tipos nas declarações de variáveis, mas isso foi fechado como "Não será corrigido" porque

Os seres humanos se beneficiam da redundância da declaração de tipo de duas maneiras. Primeiro, o tipo redundante serve como documentação valiosa - os leitores não precisam procurar a declaração de getMap () para descobrir que tipo ela retorna. Segundo, a redundância permite ao programador declarar o tipo pretendido e, assim, se beneficiar de uma verificação cruzada realizada pelo compilador.

O colaborador que encerrou isso também observou que parece "não-java", com o qual concordo. A verbosidade do Java pode ser uma bênção e uma maldição, mas torna a linguagem o que é.

É claro que aquela RFE em particular não foi o fim dessa conversa. Durante o Java 7, esse recurso foi novamente considerado , com algumas implementações de teste sendo criadas, incluindo uma pelo próprio James Gosling. Novamente, esse recurso foi finalmente derrubado.

Com o lançamento do Java 8, agora obtivemos inferência de tipo como parte de lambdas como:

List<String> names = Arrays.asList("Tom", "Dick", "Harry");
Collections.sort(names, (first, second) -> first.compareTo(second));

O compilador Java é capaz de examinar o método Collections#sort(List<T>, Comparator<? super T>)e, em seguida, a interface Comparator#compare(T o1, T o2)e determinar isso, firste seconddeve ser um String, permitindo assim que o programador renuncie à necessidade de reafirmar o tipo na expressão lambda.

Pang
fonte
5
First, the redundant type serves as valuable documentation - readers do not have to search for the declaration of getMap() to find out what type it returns- sim, se HashMap<String, Integer> map = foo.getMap()eu concordar - em C # normalmente não uso varnesses casos, mesmo que pudesse. Mas esse argumento não retém a água se for HashMap<String, Integer> map = new HashMap<String, Integer>(). Isso é redundância real e não vejo benefício em ter que escrever o nome do tipo duas vezes. E como eu me beneficiaria com uma verificação cruzada do compilador, eu não entendo nada.
Konrad Morawski
9
Sim e isso é bom para genéricos, mas ainda sinto falta do equivalente a C # varem Java.
Konrad Morawski
15
Quanto a ser "não-java", essa história (brega) vem à mente;) 9gag.com/gag/2308699/-this-is-how-things-are-done-around-here
Konrad Morawski
6
Além disso, o tipo de retorno de uma função geralmente é óbvio ou desnecessário e, mesmo nos casos em que não é seu IDE, pode sinalizar o tipo em meio segundo.
Phoshi 19/03/14
8
Acredito que a citação oficial Humans benefit from the redundancyé a verdadeira resposta para a pergunta atual, porque toda justificativa que li parece uma desculpa gratuita, infundada e ridícula para designers de Java: C # e C ++ têm o recurso, designers de C # e C ++ não são menos competentes do que Java e todos estão felizes em usá-lo. O que faz com que os desenvolvedores Java sejam diferentes dos desenvolvedores de C # ou C ++? É por isso que concordo com @KonradMorawski: "É assim que as coisas são feitas aqui" parece novamente ser a verdadeira razão dos bastidores para isso.
22468 paercebal
16

Bem, primeiro, a inferência de tipo não tem nada a ver com a maturidade do tempo de execução, seja ele uma CPU de 30 anos ou uma VM tão nova que os bits ainda são brilhantes. é tudo sobre o compilador.

Dito isso, é permitido para genéricos, a razão pela qual não é permitido para tipos não genéricos parece ser por causa da filosofia - não há nada impedindo que os designers o adicionem.

Atualização: parece que o java 10 é compatível - http://openjdk.java.net/jeps/286

jmoreno
fonte
5

Até onde eu sei, quando o Java foi projetado no início dos anos 90, a inferência de tipo não era tão popular entre as linguagens convencionais (mas já era um conceito muito conhecido, por exemplo, no ML). Então, eu posso imaginar que a inferência de tipo provavelmente não era suportada porque o Java era destinado a programadores provenientes de C ++, Pascal ou outras linguagens mainstream que não possuíam (princípio de menor surpresa).

Além disso, um dos princípios de design do Java é escrever as coisas explicitamente para garantir que o programador e o compilador tenham o mesmo entendimento do código: duplicar informações reduz as chances de erros. Obviamente, pode ser uma questão de gosto se a digitação de mais alguns caracteres vale a segurança extra que ela oferece, mas essa foi a filosofia de design seguida para Java: escreva as coisas explicitamente.

Não sei se o Java obterá inferência de tipo no futuro, mas a IMO seria uma grande revolução para a linguagem (como Glenn Nelson mencionou, foi descrito como "não-java") e, em seguida, pode-se considerar a possibilidade de descartar o nomeie Java em favor de um novo nome.

Se você deseja usar um idioma da JVM com inferência de tipo, pode usar o Scala.

Giorgio
fonte
2

Eu posso pensar em algumas razões possíveis. Uma é que a digitação explícita é auto-documentada. Java geralmente faz disso uma prioridade sobre concisão. Outro motivo pode estar nos casos em que o tipo é um tanto ambíguo. Como quando um tipo ou qualquer subtipo pode satisfazer uma rotina. Digamos que você queira usar uma lista, mas alguém aparece e usa um método exclusivo para ArrayList. O JIT inferiria um ArrayList e continuaria mesmo se você quisesse um erro de compilação.

jiggy
fonte
8
Como GridBagLayout gridbag = new GridBagLayout (); adicionar à auto-documentação? É pura repetição.
Anders Lindén
3
Desambigua de um caso em que os dois tipos não são os mesmos. Você poderia facilmente atribuir uma instância de uma subclasse.
jiggy
Let's say you want to use a List, but someone comes along and uses a method exclusive to ArrayList. The JIT would infer an ArrayList and carry on even if you wanted a compilation error- Eu não entendo isso. A inferência de tipo ocorre no momento de instanciar uma variável, não chamando um método nela. Você poderia talvez mostrar um exemplo do que quis dizer?
Konrad Morawski
2
@ KonradMorawski: Presumo que ele queira dizer que, se um método retornar ArrayList, o tipo será deduzido a isso. Se você deseja tratar um tipo como algo diferente do tipo de retorno, não pode usar inferência. Eu não entendo como isso poderia ser um problema em uma base de código em qualquer lugar perto de sã, no entanto.
Phoshi 19/03/14
o JIT não teria nenhum papel na inferência de tipos. inferência de tipo é um fenômeno em tempo de compilação. e se uma variável é declarada como uma referência a uma lista, tentando membros acesso ArrayList sobre ele não irá digitar cheque e você recebe um erro de compilação
sara
0

Isso contradiz a recomendação bem estabelecida de declarar variáveis ​​usando a interface mais genérica que atenda às suas necessidades e inicializá-las com uma classe de implementação apropriada, como em

Collection<String> names = new ArrayList<>();

Efetivamente,

var names = new ArrayList<String>();

nada mais é do que açúcar sintático para

ArrayList<String> names = new ArrayList<String>();

Se você desejar, seu IDE pode produzi-lo a partir da new ArrayList<String>()expressão com "um clique" (refatorar / criar variável local), mas lembre-se de que isso contradiz a recomendação "use interfaces".

Ralf Kleberhoff
fonte