Comparando membros enum Java: == ou equals ()?

Eu sei que enums Java são compilados para classs com construtores privados e um monte de membros estáticos públicos. Ao comparar dois membros de um dado enum, eu sempre usei .equals() , por exemplo

 public useEnums(SomeEnum a) { if(a.equals(SomeEnum.SOME_ENUM_VALUE)) { ... } ... } 

No entanto, acabei de encontrar algum código que usa o operador equals == vez de .equals ():

 public useEnums2(SomeEnum a) { if(a == SomeEnum.SOME_ENUM_VALUE) { ... } ... } 

Qual operador é aquele que eu deveria estar usando?

Ambos estão tecnicamente corretos. Se você olhar o código-fonte para .equals() , ele simplesmente irá para == .

Eu uso == , no entanto, como isso será nulo seguro.

Pode == ser usado no enum ?

Sim: os enums têm controles de instância restritos que permitem usar == para comparar instâncias. Aqui está a garantia fornecida pela especificação da linguagem (ênfase minha):

JLS 8.9 Enums

Um tipo enum não possui instâncias diferentes daquelas definidas por suas constantes enum.

É um erro em tempo de compilation para tentar explicitamente instanciar um tipo de enumeração. O método final clone no Enum garante que as constantes de enum nunca possam ser clonadas, e o tratamento especial pelo mecanismo de serialização garante que as instâncias duplicadas nunca sejam criadas como resultado da desserialização. A instanciação reflexiva de tipos enum é proibida. Juntos, essas quatro coisas garantem que nenhuma instância de um tipo enum exista além daquelas definidas pelas constantes enum .

Como há apenas uma instância de cada constante de enum , é permitido usar o operador == no lugar do método equals ao comparar duas referências de object, se soubermos que pelo menos uma delas se refere a uma constante de enum . (O método equals no Enum é um método final que simplesmente invoca super.equals em seu argumento e retorna o resultado, realizando uma comparação de identidade.)

Essa garantia é forte o suficiente para que Josh Bloch recomende que, se você insistir em usar o padrão singleton, a melhor maneira de implementá-lo é usar um enum elemento único (consulte: Effective Java 2nd Edition, Item 3: Impor a propriedade singleton com um construtor privado ou um tipo enum ; também segurança de thread no Singleton )


Quais são as diferenças entre == e equals ?

Como lembrete, é preciso dizer que geralmente == NÃO é uma alternativa viável para equals . Quando é, no entanto (como com enum ), há duas diferenças importantes a considerar:

== nunca lança NullPointerException

 enum Color { BLACK, WHITE }; Color nothing = null; if (nothing == Color.BLACK); // runs fine if (nothing.equals(Color.BLACK)); // throws NullPointerException 

== está sujeito a verificação de compatibilidade de tipo em tempo de compilation

 enum Color { BLACK, WHITE }; enum Chiral { LEFT, RIGHT }; if (Color.BLACK.equals(Chiral.LEFT)); // compiles fine if (Color.BLACK == Chiral.LEFT); // DOESN'T COMPILE!!! Incompatible types! 

Deve == ser usado quando aplicável?

Bloch menciona especificamente que classs imutáveis ​​que têm controle adequado sobre suas instâncias podem garantir aos seus clientes que == é utilizável. enum é especificamente mencionado para exemplificar.

Item 1: Considere methods de fábrica estáticos em vez de construtores

[…] permite que uma class imutável garanta que não existem duas instâncias iguais: a.equals(b) se e somente se a==b . Se uma class fizer essa garantia, seus clientes poderão usar o operador == em vez do método equals(Object) , o que pode resultar em desempenho aprimorado. Tipos Enum fornecem essa garantia.

Para resumir, os argumentos para usar == on enum são:

  • Funciona.
  • É mais rápido.
  • É mais seguro em tempo de execução.
  • É mais seguro em tempo de compilation.

Usando == para comparar dois valores enum funciona porque existe apenas um object para cada constante enum.

Em uma nota lateral, não há realmente necessidade de usar == para escrever código seguro nulo se você escrever seus equals() assim:

 public useEnums(SomeEnum a) { if(SomeEnum.SOME_ENUM_VALUE.equals(a)) { ... } ... } 

Essa é uma prática recomendada, conhecida como Comparar Constantes da esquerda, que você deve seguir.

Como outros disseram, ambos == e .equals() funcionam na maioria dos casos. A certeza de tempo de compilation que você não está comparando completamente diferentes tipos de Objetos que outros apontaram é válida e benéfica, entretanto o tipo particular de erro de comparar objects de dois tipos diferentes de tempo de compilation também seria encontrado por FindBugs (e provavelmente por Inspeções de tempo de compilation do Eclipse / IntelliJ), portanto, o compilador de Java que o encontra não adiciona muita segurança extra.

Contudo:

  1. O fato de que == nunca jogue NPE em minha mente é uma desvantagem de == . Dificilmente haverá a necessidade de os tipos de enum serem null , pois qualquer estado extra que você queira expressar via null pode ser adicionado ao enum como uma instância adicional. Se for inesperadamente null , prefiro ter um NPE do que == avaliar silenciosamente como false. Portanto, eu discordo do que é mais seguro na opinião de tempo de execução ; É melhor @Nullable o hábito de nunca deixar valores de enum serem @Nullable .
  2. O argumento de que == é mais rápido também é falso. Na maioria dos casos, você chamará .equals() em uma variável cujo tipo de tempo de compilation é a class enum e, nesses casos, o compilador pode saber que isso é igual a == (porque o método equals() enum pode não pode ser substituído) e pode otimizar a chamada da function. Eu não tenho certeza se o compilador atualmente faz isso, mas se não, e acaba por ser um problema de desempenho no Java em geral, então eu prefiro corrigir o compilador do que ter 100.000 programadores Java alterar seu estilo de programação para atender características de desempenho de uma determinada versão do compilador.
  3. enums são objects. Para todos os outros tipos de Objeto, a comparação padrão é .equals() , não == . Eu acho perigoso fazer uma exceção para enums porque você pode acabar comparando acidentalmente Objects com == vez de equals() , especialmente se você refatorar um enum em uma class não-enum. No caso de tal refatoração, o ponto de trabalho de cima está errado. Para se convencer de que o uso de == está correto, é necessário verificar se o valor em questão é um enum ou primitivo; se fosse uma class não enum , seria errado, mas fácil de perder, porque o código ainda seria compilado. O único caso em que um uso de .equals() seria errado é se os valores em questão fossem primitivos; Nesse caso, o código não seria compilado, por isso é muito mais difícil de perder. Portanto, .equals() é muito mais fácil de identificar como correto e é mais seguro contra refatorações futuras.

Eu realmente acho que a linguagem Java deveria ter definido == em Objects para chamar .equals () no valor da esquerda, e introduzir um operador separado para a identidade do object, mas não é assim que o Java foi definido.

Em resumo, ainda acho que os argumentos são a favor do uso de .equals() para tipos enum .

Aqui está um teste cronometrado para comparar os dois:

 import java.util.Date; public class EnumCompareSpeedTest { static enum TestEnum {ONE, TWO, THREE } public static void main(String [] args) { Date before = new Date(); int c = 0; for(int y=0;y<5;++y) { for(int x=0;x 

Comente os IFs um de cada vez. Aqui estão as duas comparações de cima em código de bytes desmontado:

  21 getstatic EnumCompareSpeedTest$TestEnum.ONE : EnumCompareSpeedTest.TestEnum [19] 24 getstatic EnumCompareSpeedTest$TestEnum.TWO : EnumCompareSpeedTest.TestEnum [25] 27 invokevirtual EnumCompareSpeedTest$TestEnum.equals(java.lang.Object) : boolean [28] 30 ifeq 36 36 getstatic EnumCompareSpeedTest$TestEnum.ONE : EnumCompareSpeedTest.TestEnum [19] 39 getstatic EnumCompareSpeedTest$TestEnum.TWO : EnumCompareSpeedTest.TestEnum [25] 42 if_acmpne 48 

O primeiro (igual a) executa uma chamada virtual e testa o retorno booleano da pilha. O segundo (==) compara os endereços do object diretamente da pilha. No primeiro caso, há mais atividade.

Eu corri este teste várias vezes com ambos os IFs um de cada vez. O "==" é sempre ligeiramente mais rápido.

Em caso de enum ambos estão corretos e certos !!

Eu prefiro usar == vez de equals :

Outro motivo, além dos outros já discutidos aqui, é que você pode introduzir um bug sem perceber. Suponha que você tenha este enums que é exatamente o mesmo, mas em pacakges separados (não é comum, mas pode acontecer):

Primeiro enum :

 package first.pckg public enum Category { JAZZ, ROCK, POP, POP_ROCK } 

Segundo enum:

 package second.pckg public enum Category { JAZZ, ROCK, POP, POP_ROCK } 

Então suponha que você use os iguais como próximo em item.category que é first.pckg.Category mas você importa o segundo enum ( second.pckg.Category ) em vez do primeiro sem perceber:

 import second.pckg.Category; ... Category.JAZZ.equals(item.getCategory()) 

Então você vai ter allways false devido é um enum diferente, embora você espere true porque item.getCategory() é JAZZ . E pode ser um pouco difícil de ver.

Então, se você usar o operador == você terá um erro de compilation:

operador == não pode ser aplicado a “second.pckg.Category”, “first.pckg.Category”

 import second.pckg.Category; ... Category.JAZZ == item.getCategory() 

Usar qualquer coisa diferente de == para comparar constantes enum é um triggerste. É como comparar objects de class com equals – não faça isso!

No entanto, houve um erro desagradável (BugId 6277781 ) no Sun JDK 6u10 e anterior que pode ser interessante por razões históricas. Esse bug impediu o uso adequado de == em enums desserializados, embora esse seja um caso discutível.

Enums são classs que retornam uma instância (como singletons) para cada constante de enumeração declarada pelo public static final field (imutável) para que o operador == possa ser usado para verificar sua igualdade em vez de usar o método equals()

Em suma, ambos têm prós e contras.

Por um lado, tem vantagens em usar == , conforme descrito nas outras respostas.

Por outro lado, se você por algum motivo replace as enums com uma abordagem diferente (instâncias de class normal), tendo usado == morde você. (BTDT.)

A razão pela qual os 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 enums significa que todo o seu código é fortemente acoplado ao uso desse enum.

Por exemplo: Enums pode implementar uma interface. Suponha que você esteja usando atualmente um enum que implemente o Interface1. Se mais tarde, alguém alterá-lo ou introduzir uma nova class Impl1 como uma implementação da mesma interface. Então, se você começar a usar instâncias do Impl1, você 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 qualquer ganho justificável.

Eu quero complementar a resposta dos poligenelubrificantes:

Eu pessoalmente prefiro igual (). Mas ele cumpre a verificação de compatibilidade de tipos. O que eu acho que é uma limitação importante.

Para ter verificação de compatibilidade de tipo no tempo de compilation, declare e use uma function personalizada em seu enum.

 public boolean isEquals(enumVariable) // compare constant from left public static boolean areEqual(enumVariable, enumVariable2) // compare two variable 

Com isso, você tem toda a vantagem de ambas as soluções: proteção NPE, código de leitura fácil e verificação de compatibilidade de tipo no tempo de compilation.

Eu também recomendo adicionar um valor UNDEFINED para enum.

Você pode usar: ==, equals () ou switch () para comparar Enums, todos são tecnicamente verdadeiros e atendem a sua necessidade.

Confira este tutorial para saber mais sobre as operações comuns do Enum: Como usar Enums em java

Enum no meio é um conjunto de inteiros constantes. “==” é tão válido e adequado como se você comparasse dois inteiros.

Gostaria de destacar explicitamente essa diferença específica entre o método == operator e equals() :

O método equals() serve para verificar se o conteúdo do (s) object (s) da (s) referência (s) envolvida (s) se refere é o mesmo.

O operador == verifica se as variables ​​de referência envolvidas referem- se ao mesmo object .

Cabe à class de implementação fornecer essa diferenciação conforme necessário pelo aplicativo.

Caso contrário, o comportamento padrão será o fornecido pela class Object (em Java), conforme explicado em http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/Object.html#equals ( java.lang.Object) :

O método equals para a class Object implementa a relação de equivalência possível mais discriminante em objects; ou seja, para quaisquer valores de referência não nulos x e y , esse método retorna true se, e somente se, x e y referirem ao mesmo object ( x == y tem o valor true ).