Qual é o ponto do operador de diamante (<>) no Java 7?

445

O operador de diamante no java 7 permite código como o seguinte:

List<String> list = new LinkedList<>();

No entanto, no Java 5/6, eu posso simplesmente escrever:

List<String> list = new LinkedList();

Meu entendimento do tipo apagamento é que esses são exatamente os mesmos. (O genérico é removido em tempo de execução de qualquer maneira).

Por que se preocupar com o diamante? Que nova funcionalidade / tipo de segurança ela permite? Se não gera nenhuma nova funcionalidade, por que a mencionam como um recurso? Minha compreensão desse conceito é falha?

tofarr
fonte
4
Observe que isso não é um problema se você usar métodos estáticos de fábrica, pois o Java digita inferência nas chamadas de método.
Brian Gordon
quando você desativar o aviso é realmente inútil ... :) como para mim
Renetik
3
Tem sido respondida, mas é também no tutorial Java (em torno do meio da página): docs.oracle.com/javase/tutorial/java/generics/...
Andreas Tasoulas
Bom artigo sobre isso no dZone .
R. Oosterholt
2
Minha opinião é que é um açúcar sintático para List <> lista = new LinkedList <Qualquer que seja o tipo à esquerda>> (); ou seja, mantendo-genérico
Viktor Mellgren

Respostas:

496

O problema com

List<String> list = new LinkedList();

é que, no lado esquerdo, você está usando o tipo genérico, eList<String> no lado direito, o tipo brutoLinkedList . Os tipos brutos em Java efetivamente existem apenas para compatibilidade com código pré-genérico e nunca devem ser usados ​​em novo código, a menos que você precise.

Agora, se Java tivesse genéricos desde o início e não tivesse tipos, como LinkedList, originalmente criados antes de ter genéricos, provavelmente poderia ter feito isso para que o construtor de um tipo genérico deduza automaticamente seus parâmetros de tipo da esquerda lado da tarefa, se possível. Mas não funcionou, e deve tratar os tipos brutos e genéricos de maneira diferente para compatibilidade com versões anteriores. Isso faz com que eles precisem criar uma maneira um pouco diferente , mas igualmente conveniente, de declarar uma nova instância de um objeto genérico sem precisar repetir seus parâmetros de tipo ... o operador diamante.

Quanto ao seu exemplo original List<String> list = new LinkedList(), o compilador gera um aviso para essa atribuição porque deve. Considere isto:

List<String> strings = ... // some list that contains some strings

// Totally legal since you used the raw type and lost all type checking!
List<Integer> integers = new LinkedList(strings);

Existem genéricos para fornecer proteção em tempo de compilação contra fazer a coisa errada. No exemplo acima, usar o tipo bruto significa que você não recebe essa proteção e receberá um erro no tempo de execução. É por isso que você não deve usar tipos brutos.

// Not legal since the right side is actually generic!
List<Integer> integers = new LinkedList<>(strings);

O operador de diamante, no entanto, permite que o lado direito da atribuição seja definido como uma instância genérica verdadeira com os mesmos parâmetros de tipo que o lado esquerdo ... sem precisar digitar esses parâmetros novamente. Permite manter a segurança dos genéricos com quase o mesmo esforço que o uso do tipo bruto.

Acho que o principal é entender que tipos brutos (sem <>) não podem ser tratados da mesma forma que tipos genéricos. Ao declarar um tipo bruto, você não obtém nenhum dos benefícios e a verificação de tipo de genéricos. Você também deve ter em mente que os genéricos são uma parte de uso geral da linguagem Java ... eles não se aplicam apenas aos construtores sem argumento de Collections!

ColinD
fonte
31
A compatibilidade com versões anteriores é excelente, mas não à custa da complexidade, por favor. Por que o Java 7 não pode apenas introduzir a -compatibilityopção de compilação, se ausente, javacproibirá todos os tipos brutos e aplicará apenas tipos estritamente genéricos? Isso tornará nossos códigos menos detalhados.
Rosdi Kasim
3
@Rosdi: Concordo, não há necessidade de tipos brutos no novo código. No entanto, eu preferiria fortemente incluir o número da versão Java no arquivo de origem (em vez de (mis) usar a linha de comando), veja minha resposta.
Maaartinus
37
Pessoalmente, não gosto do uso de diamantes, a menos que você esteja definindo E instanciando na mesma linha. List<String> strings = new List<>()está OK, mas se você definir private List<String> my list;e, no meio da página em que você instancia my_list = new List<>(), não será legal! O que minha lista contém novamente? Oh, deixe-me procurar a definição. De repente, o benefício do atalho de diamante se despede.
Rmabelle #
11
@rmirabelle Qual é a diferença de my_list = getTheList():? Existem várias maneiras melhores de lidar com esse tipo de problema: 1. use um IDE que mostre tipos de variáveis ​​ao passar o mouse. 2. use nomes de variáveis ​​mais significativos, como private List<String> strings3. não divida a declaração e a inicialização das variáveis, a menos que você realmente precise.
Natix 24/10
1
@Morfidon: Sim, ainda é válido para o Java 8. Tenho certeza de que você deve receber avisos.
ColinD
36

Sua compreensão é um pouco falha. O operador de diamante é um recurso interessante, pois você não precisa se repetir. Faz sentido definir o tipo uma vez quando você o declara, mas simplesmente não faz sentido defini-lo novamente no lado direito. O princípio DRY.

Agora, para explicar toda a confusão sobre a definição de tipos. Você está certo de que o tipo é removido no tempo de execução, mas uma vez que você deseja recuperar algo de uma Lista com definição de tipo, recupera-o como o tipo definido ao declarar a lista, caso contrário, perderia todos os recursos específicos e terá apenas o Recursos do objeto, exceto quando você converter o objeto recuperado para o tipo original, que às vezes pode ser muito complicado e resultar em uma ClassCastException.

Usando List<String> list = new LinkedList()você receberá avisos de tipo bruto.

Otaviano A. Damiean
fonte
8
List<String> list = new LinkedList()é o código correto. Você sabe disso e eu também. E a pergunta (como eu a entendo) é: por que apenas o compilador java não entende que esse código é bastante seguro?
Roman
22
@ Roman: nãoList<String> list = new LinkedList() é o código correto. Claro, seria bom se fosse! E provavelmente poderia ter sido se o Java tivesse genéricos desde o início e não tivesse que lidar com a compatibilidade com versões anteriores de tipos genéricos que costumavam ser não genéricos, mas possui.
colind
6
O @ColinD Java realmente não precisa lidar com a compatibilidade com versões anteriores em cada linha . Em qualquer arquivo de origem Java usando genéricos, os tipos antigos não genéricos devem ser proibidos (você sempre pode usar <?>se estiver fazendo interface com o código herdado) e o operador diamante inútil não deve existir.
Maaartinus 30/05
16

Esta linha causa o aviso [desmarcado]:

List<String> list = new LinkedList();

Portanto, a pergunta se transforma: por que o aviso [não marcado] não é suprimido automaticamente apenas no caso em que uma nova coleção é criada?

Eu acho que seria uma tarefa muito mais difícil do que adicionar um <>recurso.

UPD : Eu também acho que haveria uma confusão se fosse legalmente usar tipos brutos 'apenas para algumas coisas'.

romano
fonte
13

Em teoria, o operador diamante permite que você escreva um código mais compacto (e legível) salvando argumentos de tipo repetidos. Na prática, são apenas mais dois caracteres confusos, o que não lhe dá nada. Por quê?

  1. Nenhum programador sensato usa tipos brutos no novo código. Portanto, o compilador poderia simplesmente assumir que, ao escrever nenhum argumento de tipo, você deseja que ele os deduza.
  2. O operador de diamante não fornece informações de tipo, apenas diz que o compilador "vai ficar bem". Portanto, omitindo-o, você não pode causar danos. Em qualquer lugar em que o operador de diamante seja legal, pode ser "inferido" pelo compilador.

IMHO, ter uma maneira clara e simples de marcar uma fonte como Java 7 seria mais útil do que inventar coisas tão estranhas. No código marcado, os tipos brutos podem ser proibidos sem perder nada.

Btw., Eu não acho que isso deve ser feito usando uma opção de compilação. A versão Java de um arquivo de programa é um atributo do arquivo, nenhuma opção. Usando algo tão trivial quanto

package 7 com.example;

pode deixar claro (você pode preferir algo mais sofisticado, incluindo uma ou mais palavras-chave sofisticadas). Permitiria até compilar fontes escritas para diferentes versões Java juntas sem problemas. Isso permitiria a introdução de novas palavras-chave (por exemplo, "módulo") ou a remoção de alguns recursos obsoletos (várias classes não públicas e não aninhadas em um único arquivo ou qualquer outro) sem perder a compatibilidade.

maaartinus
fonte
2
Você já considerou a diferença entre new ArrayList(anotherList)e new ArrayList<>(anotherList)(especialmente se estiver sendo atribuído a List<String>e anotherListé a List<Integer>)?
Paul Bellora
@ Paul Bellora: Não. Surpreendentemente para mim, ambos compilam. Aquele com diamantes ainda não deu nenhum aviso. No entanto, não vejo sentido nisso, você pode elaborar?
maaartinus
Desculpe, eu não expliquei muito bem. Veja a diferença entre esses dois exemplos: ideone.com/uyHagh e ideone.com/ANkg3T Estou apenas apontando que é importante usar o operador de diamante em vez de um tipo bruto, pelo menos quando argumentos com limites genéricos estão sendo passados
Paul Bellora 17/03/2013
Na verdade, não tive tempo de ler a resposta de ColinD - ele cita praticamente a mesma coisa.
Paul Bellora 17/03/2013
2
Portanto, se estamos prestes a introduzir uma nova sintaxe para tipos brutos, para os poucos locais onde é realmente necessário, por que não usar algo parecido new @RawType List(). Essa já é uma sintaxe Java 8 válida e as anotações de tipo permitem usá-lo em todos os lugares onde for necessário, por exemplo @RawType List = (@RawType List) genericMethod();. Considerando que os tipos brutos atualmente criam um aviso do compilador, a menos que um apropriado @SuppressWarningstenha sido colocado, @RawTypeseria uma substituição razoável e uma sintaxe mais sutil não é necessária.
Holger
8

Quando você escreve List<String> list = new LinkedList(); , o compilador produz um aviso "não verificado". Você pode ignorá-lo, mas se você costumava ignorar esses avisos, também pode perder um aviso que notifica você sobre um problema de segurança real do tipo.

Portanto, é melhor escrever um código que não gere avisos extras, e o operador diamante permite que você faça isso de maneira conveniente, sem repetições desnecessárias.

axtavt
fonte
4

Tudo dito nas outras respostas é válido, mas os casos de uso não são completamente válidos para o IMHO. Se você verificar o Guava e, especialmente, o material relacionado às coleções, o mesmo será feito com os métodos estáticos. Por exemplo, Lists.newArrayList (), que permite escrever

List<String> names = Lists.newArrayList();

ou com importação estática

import static com.google.common.collect.Lists.*;
...
List<String> names = newArrayList();
List<String> names = newArrayList("one", "two", "three");

A goiaba tem outros recursos muito poderosos como esse e, na verdade, não consigo pensar em muitos usos para o <>.

Seria mais útil se eles adotassem o comportamento do operador de diamante como padrão, ou seja, o tipo é inferido do lado esquerdo da expressão ou se o tipo do lado esquerdo fosse inferido do lado direito. O último é o que acontece em Scala.

allprog
fonte
3

O ponto para o operador diamante é simplesmente reduzir a digitação do código ao declarar tipos genéricos. Não tem nenhum efeito no tempo de execução.

A única diferença se você especificar no Java 5 e 6,

List<String> list = new ArrayList();

é que você precisa especificar @SuppressWarnings("unchecked")para o list(caso contrário, receberá um aviso de elenco não verificado). Meu entendimento é que o operador de diamantes está tentando facilitar o desenvolvimento. Não tem nada a ver com a execução de genéricos em tempo de execução.

Buhake Sindi
fonte
você nem precisa usar essa anotação. Pelo menos no Eclipse, você pode simplesmente dizer ao compilador para não avisá-lo sobre isso e você está bem ...
Xerus
É melhor ter a anotação. Nem todos os desenvolvedores usam o Eclipse aqui.
Buhake Sindi