Anexando valores ao Java Enum
1. Introdução
OJava enum type fornece uma maneira com suporte de linguagem para criar e usar valores constantes. Ao definir um conjunto finito de valores,enum é mais seguro quanto ao tipo do que variáveis literais constantes comoString ouint.
However, enum values are required to be valid identifiers, e somos incentivados a usar SCREAMING_SNAKE_CASE por convenção.
Dadas essas limitações,the enum value alone is not suitable for human-readable strings or non-string values.
Neste tutorial, usaremos os recursos deenum como uma classe Java para anexar os valores que desejamos.
2. Usando JavaEnum como uma classe
Freqüentemente, criamos umenum como uma lista simples de valores. Por exemplo, aqui estão as duas primeiras linhas da tabela periódica como umenum simples:
public enum Element {
H, HE, LI, BE, B, C, N, O, F, NE
}
Usando a sintaxe acima, criamos dez instâncias finais estáticas deenum chamadasElement. Embora isso seja muito eficiente, capturamos apenas os símbolos dos elementos. E embora a forma em maiúsculas seja apropriada para constantes Java, não é como normalmente escrevemos os símbolos.
Além disso, também faltam outras propriedades dos elementos da tabela periódica, como o nome e o peso atômico.
Embora o tipoenum tenha um comportamento especial em Java, podemos adicionar construtores, campos e métodos como fazemos com outras classes. Por causa disso, podemos aprimorar nossoenum para incluir os valores de que precisamos.
3. Adicionando um Construtor e um Campo Final
Vamos começar adicionando os nomes dos elementos. We’ll set the names into a private final variable using a constructor:
public enum Element {
H("Hydrogen"),
HE("Helium"),
// ...
NE("Neon");
public final String label;
private Element(String label) {
this.label = label;
}
}
Primeiro de tudo, notamos a sintaxe especial na lista de declarações. É assim que um construtor é chamado para os tiposenum. Although it’s illegal to use the new operator for an enum, we can pass constructor arguments in the declaration list.
Em seguida, declaramos uma variável de instâncialabel. Há algumas coisas a serem observadas sobre isso.
Primeiramente, escolhemos o identificadorlabel em vez doname. Embora o campo de membroname esteja disponível para uso, vamos escolherlabel para evitar confusão com o métodoEnum.name() predefinido.
Em segundo lugar,our label field is final. While fields of an enum do not have to be final, in the majority of cases we don’t want our labels to change. No espírito dos valores deenum sendo constantes, isso faz sentido.
Finalmente, o campolabel é público. Portanto, podemos acessar o rótulo diretamente:
System.out.println(BE.label);
Por outro lado, o campo pode serprivate, acessado com um métodogetLabel(). Para fins de brevidade, este artigo continuará usando o estilo de campo público.
4. Localizando Valores JavaEnum
Java fornece um métodovalueOf(String) para todos os tiposenum. Assim, sempre podemos obter um valorenum com base no nome declarado:
assertSame(Element.LI, Element.valueOf("LI"));
No entanto, podemos querer procurar um valorenum por nosso campo de rótulo também. Para fazer isso, podemos adicionar um métodostatic:
public static Element valueOfLabel(String label) {
for (Element e : values()) {
if (e.label.equals(label)) {
return e;
}
}
return null;
}
O métodovalueOfLabel() estático itera os valoresElement até encontrar uma correspondência. Ele retornanull se nenhuma correspondência for encontrada. Por outro lado, uma exceção pode ser lançada em vez de retornarnull.
Vamos ver um exemplo rápido usando nosso métodovalueOfLabel():
assertSame(Element.LI, Element.valueOfLabel("Lithium"));
5. Cache dos valores de pesquisa
We can avoid iterating the enum values by using a Map to cache the labels. Para fazer isso, definimos umstatic final Mape o preenchemos quando a classe é carregada:
public enum Element {
// ... enum values
private static final Map BY_LABEL = new HashMap<>();
static {
for (Element e: values()) {
BY_LABEL.put(e.label, e);
}
}
// ... fields, constructor, methods
public static Element valueOfLabel(String label) {
return BY_LABEL.get(label);
}
}
As a result of being cached, the enum values are iterated only once, e o métodovalueOfLabel() é simplificado.
Como alternativa, podemos construir o cache lentamente quando ele é acessado pela primeira vez no métodovalueOfLabel(). Nesse caso, o acesso ao mapa deve ser sincronizado para evitar problemas de simultaneidade.
6. Anexando vários valores
The Enum constructor can accept multiple values. Para ilustrar, vamos adicionar o número atômico comointe o peso atômico comofloat:
public enum Element {
H("Hydrogen", 1, 1.008f),
HE("Helium", 2, 4.0026f),
// ...
NE("Neon", 10, 20.180f);
private static final Map BY_LABEL = new HashMap<>();
private static final Map BY_ATOMIC_NUMBER = new HashMap<>();
private static final Map BY_ATOMIC_WEIGHT = new HashMap<>();
static {
for (Element e : values()) {
BY_LABEL.put(e.label, e);
BY_ATOMIC_NUMBER.put(e.atomicNumber, e);
BY_ATOMIC_WEIGHT.put(e.atomicWeight, e);
}
}
public final String label;
public final int atomicNumber;
public final float atomicWeight;
private Element(String label, int atomicNumber, float atomicWeight) {
this.label = label;
this.atomicNumber = atomicNumber;
this.atomicWeight = atomicWeight;
}
public static Element valueOfLabel(String label) {
return BY_LABEL.get(label);
}
public static Element valueOfAtomicNumber(int number) {
return BY_ATOMIC_NUMBER.get(number);
}
public static Element valueOfAtomicWeight(float weight) {
return BY_ATOMIC_WEIGHT.get(weight);
}
}
Da mesma forma, podemos adicionar quaisquer valores que quisermos aoenum, como os símbolos de caso adequados, “He”, “Li” e “Be”, por exemplo.
Além disso, podemos adicionar valores calculados ao nossoenum adicionando métodos para realizar operações.
7. Controlando a Interface
Como resultado da adição de campos e métodos ao nossoenum, mudamos sua interface pública. Portanto, nosso código, que usa os métodos principaisEnumname()evalueOf(), não terá conhecimento de nossos novos campos.
O métodostaticvalueOf() já está definido para nós pela linguagem Java. Portanto, não podemos fornecer nossa própria implementaçãovalueOf().
Da mesma forma,because the Enum.name() method is final, we can’t override it either.
Como resultado, não há maneira prática de utilizar nossos campos extras usando a APIEnum padrão. Em vez disso, vamos ver algumas maneiras diferentes de expor nossos campos.
7.1. SubstituindotoString()
SubstituirtoString() pode ser uma alternativa para substituirname():
@Override
public String toString() {
return this.label;
}
Por padrão,Enum.toString() retorna o mesmo valor queEnum.name().
7.2. Implementando uma interface
The enum type in Java can implement interfaces. Embora essa abordagem não seja tão genérica quanto a APIEnum, as interfaces nos ajudam a generalizar.
Vamos considerar esta interface:
public interface Labeled {
String label();
}
Para consistência com o métodoEnum.name(), nosso métodolabel() não tem um prefixoget.
E, como o métodovalueOfLabel() éstatic, não o incluímos em nossa interface.
Finalmente, podemos implementar a interface em nossoenum:
public enum Element implements Labeled {
// ...
@Override
public String label() {
return label;
}
// ...
}
Um benefício dessa abordagem é que a interfaceLabeled pode ser aplicada a qualquer classe, não apenas aos tiposenum. Em vez de depender da APIEnum genérica, agora temos uma API mais específica ao contexto.
8. Conclusão
Neste artigo, exploramos muitos recursos da implementação JavaEnum. Ao adicionar construtores, campos e métodos, vemos queenum pode fazer muito mais do que constantes literais.
Como sempre, o código-fonte completo deste artigo pode ser encontrado emGithub.