Qual é a vantagem de um enum Java versus uma classe com campos finais estáticos públicos?

147

Eu estou muito familiarizado com o C #, mas começando a trabalhar mais em Java. Eu esperava aprender que as enums em Java eram basicamente equivalentes às do C #, mas aparentemente esse não é o caso. Inicialmente, fiquei empolgado ao saber que as enumerações Java podiam conter vários dados, o que parece muito vantajoso ( http://docs.oracle.com/javase/tutorial/java/javaOO/enum.html ). No entanto, desde então, encontrei muitos recursos ausentes que são triviais em C #, como a capacidade de atribuir facilmente um elemento de enum a um determinado valor e, consequentemente, a capacidade de converter um número inteiro em enum sem uma quantidade razoável de esforço ( ie Converter valor inteiro para Java Enum correspondente ).

Então, minha pergunta é a seguinte: existe algum benefício para as enumerações de Java sobre uma classe com vários campos finais estáticos públicos? Ou apenas fornece uma sintaxe mais compacta?

EDIT: Deixe-me ser mais claro. Qual é o benefício das enumerações Java sobre uma classe com vários campos finais estáticos públicos do mesmo tipo ? Por exemplo, no exemplo de planetas no primeiro link, qual é a vantagem de um enum sobre uma classe com essas constantes públicas:

public static final Planet MERCURY = new Planet(3.303e+23, 2.4397e6);
public static final Planet VENUS = new Planet(4.869e+24, 6.0518e6);
public static final Planet EARTH = new Planet(5.976e+24, 6.37814e6);
public static final Planet MARS = new Planet(6.421e+23, 3.3972e6);
public static final Planet JUPITER = new Planet(1.9e+27, 7.1492e7);
public static final Planet SATURN = new Planet(5.688e+26, 6.0268e7);
public static final Planet URANUS = new Planet(8.686e+25, 2.5559e7);
public static final Planet NEPTUNE = new Planet(1.024e+26, 2.4746e7);

Tanto quanto posso dizer, a resposta de casablanca é a única que satisfaz isso.

Craig W
fonte
4
@ Bohemian: Pode não ser uma duplicata, uma vez que o OP apenas mencionou public static finalcampos, que podem ser valores digitados e não necessariamente ints.
Casablanca #
1
@ Shahzeb Hardly. Claramente, usar enums em vez de constantes de string é uma idéia EXTREMAMENTE boa e mais do que incentivada. Segurança de tipo, não precisa de funções de utilidade estática e assim por diante .. absolutamente nenhuma razão para usar seqüências de caracteres.
Voo
1
@ Voo Sim, eu sabia que haverá divergências. E aqui está uma em 49 segundos. As enums são ótimas (e eu as amo e as uso com muita frequência), mas de que tipo de segurança você precisa ao declarar uma constante ou usá-la. É um exagero criar enum sempre que você precisar declarar uma constante para String literal.
Shahzeb
4
@ Shahzeb Se você tiver uma única variável, com certeza use uma string, não pode acontecer muita coisa aqui (um único valor é inútil como parâmetro). Mas observe que estamos falando de constante S, então agora estamos falando de provavelmente passá-los para funções, etc. Precisamos de segurança de tipo? Bem, não, mas a maioria das pessoas considera os tipos c de estilo "tudo é um void*" bom e isso pode impedir bugs (especialmente se você passar mais de um parâmetro enum / string!). Além disso, coloca as constantes em seu próprio espaço para nome, etc. Ao contrário disso, não há vantagem real em ter apenas variáveis ​​simples.
Voo 02/02
3
@ Bohemian: Não vejo como. Com ints, não existe um tipo de segurança, pois é possível transmitir qualquer valor. Os objetos digitados, por outro lado, não são diferentes das enumerações em termos de segurança de tipo.
casablanca

Respostas:

78

Tecnicamente, pode-se ver enum como uma classe com um monte de constantes digitadas, e é assim que as constantes enum são implementadas internamente. O uso de um enumno entanto fornece métodos úteis ( Enum javadoc ) que você teria que implementar de outra maneira, como Enum.valueOf.

Casablanca
fonte
14
Também é .values()necessário percorrer a lista de valores.
H3xStream
1
Esta parece ser a resposta certa, embora não seja muito satisfatória. Na minha opinião, mal valia a pena o Java adicionar suporte a enums apenas para uma sintaxe mais compacta e acesso aos métodos Enum.
Craig W
7
@Craig seus instintos estão certos - esta é uma resposta muito ruim, porque perdeu totalmente o objetivo das enums. Veja meus comentários na pergunta por parte das razões.
Bohemian
1
@ Bohemian: Eu não "errei o objetivo" das enums - eu as uso o tempo todo. Veja minha resposta ao seu comentário acima.
Casablanca #
1
O método Enum.valuesOf retorna o mesmo objeto se chamado duas vezes?
Emre Aktürk
104
  1. Digite segurança e valorize a segurança.
  2. Singleton garantido.
  3. Capacidade de definir e substituir métodos.
  4. Capacidade de usar valores na switchdeclaraçãocase declaração sem qualificação.
  5. Sequencialização incorporada de valores via ordinal().
  6. Serialização por nome, não por valor, o que oferece um grau de proteção para o futuro.
  7. EnumSete EnumMapaulas.
Marquês de Lorne
fonte
19
Dito tudo isso, toda vez que coloco código em um Enum, me arrependo.
Marquês de Lorne
4
Por que você se arrependeu? Eu nunca fiz ...
glglgl
2
@glglgl Porque coloquei o código específico do aplicativo em um lugar onde eu senti que realmente não pertencia, que estava apenas definindo um conjunto de valores. Se eu tivesse que fazer de novo, eu o incluiria em uma das inúmeras switchdeclarações que foram a motivação original para o uso de uma Enum.
Marquês de Lorne,
72

Ninguém mencionou a capacidade de usá-los em switchdeclarações; Vou jogar isso também.

Isso permite que enumerações arbitrariamente complexas sejam usadas de maneira limpa, sem o uso de sequências instanceofpotencialmente confusas ifou valores de comutação sem string / int. O exemplo canônico é uma máquina de estado.

Dave Newton
fonte
De qualquer forma, você não mencionou nenhum benefício de enumerações versus campos estáticos. Você pode usar tipos suficientes em instruções de opção com campos estáticos. Op necessidade real funcionais ou perfomance diferenças
Genaut
@Genaut O benefício é que as enums têm mais funcionalidade do que uma string ou int - a questão era sobre diferenças, que eu forneci. O OP já está ciente do que são enums, e ninguém mais mencionou as declarações de switch quando eu publiquei isso há 4,5 anos, e pelo menos algumas pessoas descobriram que ele fornecia novas informações ¯_ (ツ) _ / ¯
Dave Newton
44

A principal vantagem é a segurança do tipo. Com um conjunto de constantes, qualquer valor do mesmo tipo intrínseco pode ser usado, introduzindo erros. Com uma enumeração, apenas os valores aplicáveis ​​podem ser usados.

Por exemplo

public static final int SIZE_SMALL  = 1;
public static final int SIZE_MEDIUM = 2;
public static final int SIZE_LARGE  = 3;

public void setSize(int newSize) { ... }

obj.setSize(15); // Compiles but likely to fail later

vs

public enum Size { SMALL, MEDIUM, LARGE };

public void setSize(Size s) { ... }

obj.setSize( ? ); // Can't even express the above example with an enum
Jim Garrison
fonte
3
classes também são do tipo seguro ...: (campo assumindo estática são do tipo da classe container) /
h3xStream
Você ainda pode transmitir um valor inválido, mas seria um erro de tempo de compilação que é muito mais fácil de detectar.
Zzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
2
Você poderia chamar setSize(null)seu segundo exemplo, mas é provável que falhe muito antes do erro do primeiro exemplo.
11134 Jeffrey
42

Há menos confusão. Tome Fontpor exemplo. Ele possui um construtor que leva o nome do que Fontvocê deseja, seu tamanho e estilo ( new Font(String, int, int)). Até hoje não me lembro se o estilo ou o tamanho vão primeiro. Se Fonttivesse usado um enumpara todos os seus diferentes estilos ( PLAIN, BOLD, ITALIC, BOLD_ITALIC), seu construtor seria semelhante Font(String, Style, int), evitando qualquer confusão. Infelizmente, enumnão existiam quando oFont existia classe foi criada e, como o Java precisa manter a compatibilidade reversa, sempre seremos afetados por essa ambiguidade.

Obviamente, este é apenas um argumento para usar constantes em enumvez de public static finalconstantes. As enums também são perfeitas para singletons e implementam o comportamento padrão, permitindo uma personalização posterior (ou seja, o padrão de estratégia ). Um exemplo do último é java.nio.file's OpenOptione StandardOpenOption: se um desenvolvedor quisesse criar seu próprio não-padrão OpenOption, ele poderia.

Jeffrey
fonte
Seu caso 'Fonte' ainda é ambíguo se duas das mesmas enumerações forem solicitadas. A resposta canônica para esse problema é o que muitos idiomas chamam de parâmetros nomeados . Esse ou melhor suporte à assinatura do método IDE.
Aaaaaa
1
@aaaaaa Eu não vi muitos casos em que um construtor pegaria dois iguais enumsem usar varargs ou a Setpara pegar um número arbitrário deles.
21915 Jeffrey
@aaaaaa O principal problema dos parâmetros nomeados é o de detalhes de implementação (o nome do parâmetro). Eu posso fazer uma interface interface Foo { public void bar(String baz); }. Alguém faz uma classe que invoca bar: someFoo.bar(baz = "hello");. Eu mudo a assinatura de Foo::barpara public void bar(String foobar). Agora, a pessoa que ligou someFooprecisará modificar seu código, se ainda quiser que ele funcione.
21415 Jeffrey
Também não me lembro de ver tipos de enum consecutivos, mas pensei que um comum poderia ser DAY_OF_WEEK ou algo semelhante. E um bom argumento sobre a interface - não tinha pensado nisso. Pessoalmente, eu consideraria esse problema a ambiguidade generalizada causada por parâmetros sem nome, necessitando de um suporte IDE mais forte. No entanto, eu entendo que é uma decisão judicial e que quebrar as alterações da API é algo a considerar seriamente.
Aaaaaa
26

Há muitas respostas boas aqui, mas nenhuma menciona que há implementações altamente otimizadas das classes / interfaces da API Collection especificamente para enumerações :

Essas classes específicas de enum aceitam apenas Enuminstâncias (as EnumMapúnicasEnum apenas como chaves) e, sempre que possível, elas revertem para compactar representação e manipulação de bits em sua implementação.

O que isto significa?

Se nosso Enumtipo não tiver mais que 64 elementos (a maioria dos Enumexemplos da vida real se qualificará para isso), as implementações armazenam os elementos em um único longvalor, cada Enuminstância em questão será associada a um pouco desse comprimento de 64 bits long. Adicionar um elemento a um EnumSeté simplesmente definir o bit apropriado como 1, removê-lo é apenas definir esse bit como 0. Testar se um elemento está no Seté apenas um teste de máscara de bit ! Agora você tem amor Enumpor isso!

icza
fonte
1
Eu sabia sobre esses dois antes, mas aprendi muito com a sua What does this mean?seção. Eu sabia que havia uma divisão na implementação no tamanho 64, mas eu realmente não sei por que
Christopher Rucinski
15

exemplo:

public class CurrencyDenom {
   public static final int PENNY = 1;
 public static final int NICKLE = 5;
 public static final int DIME = 10;
public static final int QUARTER = 25;}

Limitação de constantes java

1) Sem segurança de tipo : Primeiro de tudo, não é seguro de tipo; você pode atribuir qualquer valor int válido a int, por exemplo, 99, embora não exista uma moeda para representar esse valor.

2) Nenhuma impressão significativa : o valor de impressão de qualquer uma dessas constantes imprimirá seu valor numérico em vez do nome significativo da moeda; por exemplo, quando você imprime NICKLE, ele imprime "5" em vez de "NICKLE"

3) Sem espaço para nome : para acessar a constante currencyDenom, precisamos prefixar o nome da classe, por exemplo, CurrencyDenom.PENNY, em vez de apenas usar PENNY, embora isso também possa ser alcançado usando a importação estática no JDK 1.5

Vantagem de enum

1) As enums em Java são de tipo seguro e possuem um espaço de nome próprio. Isso significa que sua enumeração terá um tipo, por exemplo, "Moeda" no exemplo abaixo e você não poderá atribuir nenhum valor diferente do especificado em Constantes de enumeração.

public enum Currency {PENNY, NICKLE, DIME, QUARTER};

Currency coin = Currency.PENNY; coin = 1; //compilation error

2) O Enum em Java é do tipo referência, como classe ou interface, e você pode definir construtor, métodos e variáveis ​​dentro do Java Enum, o que o torna mais poderoso que o Enum em C e C ++, como mostra o próximo exemplo do tipo Java Enum.

3) Você pode especificar valores de constantes de enumeração no momento da criação, como mostrado no exemplo abaixo: enumeração pública Moeda {PENNY (1), NICKLE (5), DIME (10), QUARTER (25)}; Mas, para que isso funcione, você precisa definir uma variável de membro e um construtor porque PENNY (1) está realmente chamando um construtor que aceita o valor int, veja o exemplo abaixo.

public enum Currency {
    PENNY(1), NICKLE(5), DIME(10), QUARTER(25);
    private int value;

    private Currency(int value) {
            this.value = value;
    }
}; 

Referência: https://javarevisited.blogspot.com/2011/08/enum-in-java-example-tutorial.html

Maverick
fonte
11

O primeiro benefício das enums, como você já notou, é a simplicidade da sintaxe. Mas o principal ponto de enumeração é fornecer um conjunto conhecido de constantes que, por padrão, formam um intervalo e ajudam a executar uma análise de código mais abrangente por meio de verificações de segurança de tipo e valor.

Esses atributos de enumerações ajudam um programador e um compilador. Por exemplo, digamos que você veja uma função que aceita um número inteiro. O que esse número inteiro pode significar? Que tipo de valores você pode transmitir? Você realmente não sabe imediatamente. Mas se você vê uma função que aceita enum, conhece muito bem todos os valores possíveis que pode transmitir.

Para o compilador, as enumerações ajudam a determinar um intervalo de valores e, a menos que você atribua valores especiais aos membros da enumeração, elas também variam de 0 a mais. Isso ajuda a rastrear automaticamente os erros no código por meio de verificações de segurança de tipo e muito mais. Por exemplo, o compilador pode avisá-lo de que você não manipula todos os valores possíveis de enumeração na instrução switch (ou seja, quando você não possuidefault maiúsculas e minúsculas e manipula apenas um dos valores de enum). Ele também avisa quando você converte um número inteiro arbitrário em enum porque o intervalo de valores do enum é menor que o número inteiro e que, por sua vez, pode desencadear erros na função que realmente não aceita um número inteiro. Além disso, a geração de uma tabela de salto para o comutador se torna mais fácil quando os valores são de 0 ou mais.

Isso não é verdade apenas para Java, mas também para outras linguagens com uma rigorosa verificação de tipo. C, C ++, D, C # são bons exemplos.


fonte
4

Uma enum é implicitamente final; com construtores privados, todos os seus valores são do mesmo tipo ou sub-tipo; você pode obter todos os seus valores usando values(), obtém seu name()ou ordinal()valor ou pode procurar uma enum por número ou nome.

Você também pode definir subclasses (mesmo que nocionalmente final, algo que você não pode fazer de outra maneira)

enum Runner implements Runnable {
    HI {
       public void run() {
           System.out.println("Hello");
       }
    }, BYE {
       public void run() {
           System.out.println("Sayonara");
       }
       public String toString() {
           return "good-bye";
       }
    }
 }

 class MYRunner extends Runner // won't compile.
Peter Lawrey
fonte
4

enum Benefícios:

  1. Enums são de tipo seguro, campos estáticos não são
  2. Há um número finito de valores (não é possível passar um valor de enum inexistente. Se você possui campos de classe estáticos, pode cometer esse erro)
  3. Cada enum pode ter várias propriedades (campos / getters) atribuídas - encapsulamento. Também alguns métodos simples: YEAR.toSeconds () ou similar. Compare: Colors.RED.getHex () com Colors.toHex (Colors.RED)

"como a capacidade de atribuir facilmente um elemento enum a um determinado valor"

enum EnumX{
  VAL_1(1),
  VAL_200(200);
  public final int certainValue;
  private X(int certainValue){this.certainValue = certainValue;}
}

"e, consequentemente, a capacidade de converter um número inteiro em uma enum sem uma quantidade razoável de esforço" Adicione um método que converta int em enum que faça isso. Apenas adicione HashMap estático <Inteiro, EnumX> contendo o mapeamento java enum .

Se você realmente deseja converter ord = VAL_200.ordinal () de volta em val_200, use: EnumX.values ​​() [ord]

mabn
fonte
3

Outra diferença importante é que o compilador java trata static finalcampos de tipos primitivos e String como literais. Isso significa que essas constantes se tornam inline. É semelhante ao C/C++ #definepré - processador. Veja esta pergunta SO . Este não é o caso de enums.

Seyed Jalal Hosseini
fonte
2

Você obtém uma verificação do tempo de compilação de valores válidos quando você usa uma enumeração. Olhe para esta pergunta.

zzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
fonte
2

A maior vantagem é enum Os singletons são fáceis de escrever e seguros para threads:

public enum EasySingleton{
    INSTANCE;
}

e

/**
* Singleton pattern example with Double checked Locking
*/
public class DoubleCheckedLockingSingleton{
     private volatile DoubleCheckedLockingSingleton INSTANCE;

     private DoubleCheckedLockingSingleton(){}

     public DoubleCheckedLockingSingleton getInstance(){
         if(INSTANCE == null){
            synchronized(DoubleCheckedLockingSingleton.class){
                //double checking Singleton instance
                if(INSTANCE == null){
                    INSTANCE = new DoubleCheckedLockingSingleton();
                }
            }
         }
         return INSTANCE;
     }
}

ambos são semelhantes e lidou com a serialização por si mesmos, implementando

//readResolve to prevent another instance of Singleton
    private Object readResolve(){
        return INSTANCE;
    }

Mais

Premraj
fonte
0

Eu acho enumque não pode ser final, porque sob o capô o compilador gera subclasses para cada enumentrada.

Mais informações Da fonte

Sitansu
fonte
Internamente, eles não são finais, porque - como você diz - pode ser subclassificado internamente. Mas, infelizmente, você não pode subclassificá-los por conta própria, por exemplo, para estendê-lo com valores próprios.
glglgl
0

Há muitas vantagens de enumerações postadas aqui e estou criando essas enumerações agora, conforme solicitado na pergunta. Mas eu tenho um enum com 5-6 campos.

enum Planet{
EARTH(1000000, 312312321,31232131, "some text", "", 12),
....
other planets
....

Nesses tipos de casos, quando você tem vários campos em enumerações, é muito difícil entender qual valor pertence a qual campo, pois você precisa ver o construtor e a bola ocular.

Classificar com static finalconstantes e usar o Builderpadrão para criar esses objetos o torna mais legível. Mas você perderia todas as outras vantagens de usar uma enumeração, se precisar delas. Uma desvantagem de tais classes é que você precisa adicionar os Planetobjetos manualmente ao list/setdePlanets.

Eu ainda prefiro enum sobre essa classe, como values()vem a calhar e você nunca sabe se você precisar deles para usar em switchou EnumSetou EnumMapno futuro :)

Bikas Katwal
fonte
0

Razão principal: as enums ajudam você a escrever um código bem estruturado, onde o significado semântico dos parâmetros é claro e fortemente digitado em tempo de compilação - por todos os motivos que outras respostas deram.

Quid pro quo: no Java pronto para uso, a matriz de membros da Enum é final. Isso normalmente é bom, pois ajuda a valorizar a segurança e os testes, mas em algumas situações pode ser uma desvantagem, por exemplo, se você estiver estendendo o código base existente, talvez a partir de uma biblioteca. Por outro lado, se os mesmos dados estiverem em uma classe com campos estáticos, você poderá adicionar facilmente novas instâncias dessa classe em tempo de execução (também será necessário escrever código para adicioná-los a qualquer Iterável que você tiver para essa classe). Mas esse comportamento do Enums pode ser alterado: usando a reflexão, você pode adicionar novos membros em tempo de execução ou substituir membros existentes, embora isso provavelmente deva ser feito apenas em situações especializadas em que não há alternativa: por exemplo, é uma solução hacky e pode produzir problemas inesperados, veja minha resposta emPosso adicionar e remover elementos de enumeração em tempo de execução em Java .

radfast
fonte