Eu sei que as enumerações Java são compiladas em classes com construtores privados e um monte de membros estáticos públicos. Ao comparar dois membros de uma determinada enumeração, sempre usei .equals()
, por exemplo
public useEnums(SomeEnum a)
{
if(a.equals(SomeEnum.SOME_ENUM_VALUE))
{
...
}
...
}
No entanto, acabei de encontrar um código que usa o operador equals em ==
vez de .equals ():
public useEnums2(SomeEnum a)
{
if(a == SomeEnum.SOME_ENUM_VALUE)
{
...
}
...
}
Qual operador é o que eu devo usar?
Respostas:
Ambos são tecnicamente corretos. Se você procurar o código-fonte
.equals()
, ele simplesmente adia==
.Eu uso
==
, no entanto, como isso será nulo seguro.fonte
.equals()
inseri-lo? Eu sei que não.Pode
==
ser usadoenum
?Sim: as enumerações possuem controles de instância rígidos que permitem usar
==
para comparar instâncias. Aqui está a garantia fornecida pela especificação de idioma (ênfase minha):Essa garantia é suficientemente forte que Josh Bloch recomenda que, se você insistir em usar o padrão singleton, a melhor maneira de implementá-lo é usar um elemento único
enum
(consulte: Java Efetivo 2ª Edição, Item 3: Aplicar a propriedade singleton com um construtor privado ou um tipo de enum ; também Segurança de threads em Singleton )Quais são as diferenças entre
==
eequals
?Como um lembrete, é preciso dizer que, geralmente,
==
NÃO é uma alternativa viávelequals
. Quando é, no entanto (como comenum
), há duas diferenças importantes a serem consideradas:==
nunca jogaNullPointerException
==
está sujeito à verificação de compatibilidade de tipo em tempo de compilaçãoDeve
==
ser usado quando aplicável?Bloch menciona especificamente que classes imutáveis que têm controle adequado sobre suas instâncias podem garantir a seus clientes que
==
é utilizável.enum
é especificamente mencionado para exemplificar.Para resumir, os argumentos para usar
==
onenum
são:fonte
Color nothing = null;
IMHO é um bug e deve ser corrigido, sua enumeração tem dois valores, não três ( veja o comentário de Tom Hawtin) 4. É mais seguro em tempo de compilação : OK, masequals
ainda assim retornariafalse
. No final, eu não compro, prefiroequals()
a legibilidade (veja o comentário de Kevin).foo.equals(bar)
é mais legível do quefoo == bar
Usar
==
para comparar dois valores de enumeração funciona, porque existe apenas um objeto para cada constante de enumeração.Em uma nota lateral, na verdade não há necessidade de
==
se escrever códigos com segurança nula, se você escreverequals()
assim:Esta é uma prática recomendada conhecida como Comparar constantes da esquerda que você definitivamente deve seguir.
fonte
.equals()
inserir valores de string, mas ainda acho que é uma maneira ruim de escrever código com segurança nula. Eu preferiria ter um operador (ou um método, mas sei que [objeto nulo] .equals nunca será nulo-seguro em Java por causa de como o operador ponto funciona) que é sempre nulo-seguro, independentemente do ordem em que é usado. Semântica, o operador "igual" deve sempre comutar.?.
), continuarei a usar essa prática ao comparar com constantes e, TBH, não a acho ruim, talvez apenas menos "natural".null
enum geralmente é um erro. Você tem essa enumeração de todos os valores possíveis, e aqui está outro!@Nullable
, considero Comparar constantes da esquerda um antipadrão, porque espalha ambiguidade sobre quais valores podem ou não ser nulos. Os tipos de enumeração quase nunca devem ser@Nullable
, portanto, um valor nulo seria um erro de programação; nesse caso, quanto mais cedo você lançar o NPE, maior será a probabilidade de encontrar seu erro de programação em que você permitiu que ele fosse nulo. Portanto, "as melhores práticas ... que você definitivamente deve seguir" está totalmente errada quando se trata de tipos de enumeração.Como já foi dito, tanto
==
e.equals()
de trabalho na maioria dos casos. A certeza do tempo de compilação de que você não está comparando tipos completamente diferentes de objetos que outras pessoas apontaram é válida e benéfica; no entanto, o tipo específico de erro de comparar objetos de dois tipos diferentes de tempo de compilação também seria encontrado pelo FindBugs (e provavelmente por Inspeções de tempo de compilação do Eclipse / IntelliJ), para que o compilador Java que o encontra não adicione muita segurança extra.Contudo:
==
não joga NPE em minha mente é uma desvantagem de==
. Dificilmente deve haver a necessidade deenum
tiposnull
, uma vez que qualquer estado extra que você queira expressar vianull
pode ser adicionado àenum
instância como uma instância adicional. Se for inesperadonull
, prefiro ter um NPE do que==
avaliar silenciosamente como falso. Portanto, eu discordo da opinião mais segura na hora da execução ; é melhor adquirir o hábito de nunca deixar osenum
valores acontecerem@Nullable
.==
é mais rápido também é falso. Na maioria dos casos, você chamará.equals()
uma variável cujo tipo de tempo de compilação é a classe enum e, nesses casos, o compilador poderá saber que é o mesmo que==
(porqueenum
oequals()
método de um não pode ser substituído) e otimizar a chamada de função longe. Não tenho certeza se o compilador atualmente faz isso, mas se não o fizer, e se mostra um problema de desempenho em geral no Java, prefiro corrigir o compilador do que 100.000 programadores Java alterar seu estilo de programação para se adequarem características de desempenho de uma versão específica do compilador.enums
são objetos. Para todos os outros tipos de objetos, a comparação padrão é.equals()
não==
. Eu acho que é perigoso abrir uma exceçãoenums
porque você pode comparar objetos acidentalmente com, em==
vez deequals()
, especialmente se você refatorar umaenum
para uma classe não enum. No caso de tal refatoração, o ponto de funcionamento acima é incorreto. Para se convencer de que o uso de==
é correto, é necessário verificar se o valor em questão é umenum
ou um primitivo; se não pertencesse àenum
classe, seria errado, mas fácil de perder, porque o código ainda seria compilado. O único caso em que o uso de.equals()
estaria errado é se os valores em questão fossem primitivos; nesse caso, o código não seria compilado, então é muito mais difícil não entender. Portanto,.equals()
é muito mais fácil identificar como correto e mais seguro contra refatorações futuras.Na verdade, acho que a linguagem Java deveria ter definido == on Objects para chamar .equals () no valor à esquerda e introduzir um operador separado para a identidade do objeto, mas não foi assim que o Java foi definido.
Em resumo, ainda acho que os argumentos são a favor do uso
.equals()
deenum
tipos.fonte
enum
comoswitch
alvo, para que sejam um pouco especiais.String
s são um pouco especiais também.Eu prefiro usar em
==
vez deequals
:Outro motivo, além dos outros já discutidos aqui, é que você pode introduzir um bug sem perceber. Suponha que você tenha essas enumerações exatamente iguais, mas em pacakges separados (não é comum, mas pode acontecer):
Primeira enumeração :
Segunda enumeração:
Então, suponha que você use os iguais como next em
item.category
que é,first.pckg.Category
mas importe o segundo enum (second.pckg.Category
) em vez do primeiro sem perceber:Portanto, você receberá sempre
false
uma enumeração diferente, embora você espere verdadeiro porqueitem.getCategory()
éJAZZ
. E pode ser um pouco difícil de ver.Portanto, se você usar o operador
==
, terá um erro de compilação:fonte
Aqui está um teste de tempo bruto para comparar os dois:
Comente os FIs, um de cada vez. Aqui estão as duas comparações acima em código de bytes desmontado:
O primeiro (igual) executa uma chamada virtual e testa o retorno booleano da pilha. O segundo (==) compara os endereços dos objetos diretamente da pilha. No primeiro caso, há mais atividade.
Eu executei esse teste várias vezes com os dois IFs, um de cada vez. O "==" é sempre um pouco mais rápido.
fonte
Em caso de enum, ambos estão corretos e corretos !!
fonte
tl; dr
Outra opção é o
Objects.equals
método utilitário.Objects.equals
para segurança nulaUma terceira opção é o
equals
método estático encontrado naObjects
classe de utilitário adicionada ao Java 7 e posterior.Exemplo
Aqui está um exemplo usando o
Month
enum.Benefícios
Encontro alguns benefícios para esse método:
true
false
NullPointerException
Como funciona
Qual é a lógica usada por
Objects.equals
?Veja você mesmo, no código-fonte do Java 10 do OpenJDK :
fonte
Usar algo diferente de
==
comparar constantes enum é um absurdo. É como compararclass
objetos comequals
- não faça isso!No entanto, houve um erro grave (BugId 6277781 ) no Sun JDK 6u10 e versões anteriores que podem ser interessantes por razões históricas. Esse bug impediu o uso adequado de
==
enumerações desserializadas, embora isso possa ser discutido de certa forma.fonte
Enums são classes que retornam uma instância (como singletons) para cada constante de enumeração declarada por
public static final field
(imutável) para que o==
operador possa ser usado para verificar sua igualdade em vez de usar oequals()
métodofonte
A razão pela qual as enums funcionam facilmente com == é porque cada instância definida também é um singleton. Portanto, a comparação de identidade usando == sempre funcionará.
Mas usar == porque funciona com enumerações significa que todo o seu código está fortemente associado ao uso dessa enumeração.
Por exemplo: Enums podem implementar uma interface. Suponha que você esteja atualmente usando uma enumeração que implementa a Interface1. Se, posteriormente, alguém a alterar ou introduzir uma nova classe Impl1 como uma implementação da mesma interface. Então, se você começar a usar instâncias do Impl1, terá muito código para alterar e testar devido ao uso anterior de ==.
Portanto, é melhor seguir o que é considerado uma boa prática, a menos que haja algum ganho justificável.
fonte
Uma das regras do Sonar é
Enum values should be compared with "=="
. As razões são as seguintes:Por último, mas não menos importante, as
==
enumerações on são indiscutivelmente mais legíveis (menos detalhadas) queequals()
.fonte
Em suma, ambos têm prós e contras.
Por um lado, possui vantagens de usar
==
, conforme descrito nas outras respostas.Por outro lado, se você, por qualquer motivo, substituir as enumerações por uma abordagem diferente (instâncias normais de classe),
==
você terá mordido. (BTDT.)fonte
Quero complementar a resposta de poligenelubricants:
Eu pessoalmente prefiro igual a (). Mas ele verifica o tipo de verificação de compatibilidade. O que eu acho que é uma limitação importante.
Para ter a verificação de compatibilidade de tipo no momento da compilação, declare e use uma função personalizada na sua enumeração.
Com isso, você obtém todas as vantagens de ambas as soluções: proteção NPE, fácil leitura de código e verificação de compatibilidade de tipo no momento da compilação.
Também recomendo adicionar um valor indefinido para enum.
fonte
==
? Com==
você obtém proteção NPE, código de fácil leitura e verificação de compatibilidade de tipo de tempo de compilação, e você obtém tudo isso sem escrever nenhum método semelhante a igual personalizado.UNDEFINED
valor é provavelmente uma boa ideia. Obviamente, no Java 8 você usaria umOptional
.