Resuma o uso de enumerações no trabalho

1. Visão Geral

Neste artigo, veremos o que são enumerações Java, quais problemas elas resolvem e como usar enumerações Java para implementar alguns padrões de projeto na prática.

A palavra-chave enum foi introduzida em java5 para indicar um tipo especial de classe, que sempre herda a classe java.lang.Enum, e você pode verificar sua documentação oficial para obter mais conteúdo.

As enumerações são freqüentemente comparadas com constantes, provavelmente porque realmente usamos enumerações em grandes números para substituir constantes. Então, quais são as vantagens dessa abordagem?

Constantes definidas desta forma tornam o código mais legível, permitem verificações em tempo de compilação, pré-gravam uma lista de valores aceitáveis ​​e evitam comportamento inesperado devido a valores inválidos passados.

O exemplo a seguir define o status de um pedido de pizza do tipo enumerado simples. Existem três estados de PEDIDO, PRONTO e ENTREGUE:

package shuang.kou.enumdemo.enumtest;

publicenum PizzaStatus {
    ORDERED,
    READY,
    DELIVERED;
}

Para simplificar, evitamos definir constantes por meio do código acima. Colocamos todas as constantes relacionadas ao status do pedido de pizza em um tipo enumerado.

System.out.println(PizzaStatus.ORDERED.name());//ORDERED
System.out.println(PizzaStatus.ORDERED);//ORDERED
System.out.println(PizzaStatus.ORDERED.name().getClass());//class java.lang.String
System.out.println(PizzaStatus.ORDERED.getClass());//class shuang.kou.enumdemo.enumtest.PizzaStatus

2. Método de enumeração personalizado

Agora que temos um entendimento básico do que são enums e como usá-los, vamos levar o exemplo anterior a um novo nível, definindo alguns métodos de API adicionais em enums:

publicclass Pizza {
    private PizzaStatus status;
    publicenum PizzaStatus {
        ORDERED,
        READY,
        DELIVERED;
    }
 
    public boolean isDeliverable() {
        if (getStatus() == PizzaStatus.READY) {
            returntrue;
        }
        returnfalse;
    }
     
    // Methods that set and get the status variable.
}

3. Use == para comparar os tipos enumerados

Uma vez que o tipo de enumeração garante que haja apenas uma instância constante na JVM, podemos usar com segurança o operador "==" para comparar duas variáveis, conforme mostrado no exemplo acima; além disso, o operador "==" pode fornecer compilar - Segurança em tempo e execução.

Primeiro, vamos dar uma olhada na segurança de tempo de execução no seguinte trecho de código, onde o operador "==" é usado para comparar estados e se ambos os valores forem nulos, NullPointerException não será lançado. Pelo contrário, se o método equals for usado, uma NullPointerException será lançada:

if(testPz.getStatus().equals(Pizza.PizzaStatus.DELIVERED));
if(testPz.getStatus() == Pizza.PizzaStatus.DELIVERED);

Para segurança em tempo de compilação, vamos olhar outro exemplo. Dois tipos de enumeração diferentes são comparados. O resultado da comparação usando o método equal é determinado como verdadeiro, porque o valor de enumeração do método getStatus é consistente com o valor de enumeração de outro tipo, mas logicamente deve ser falso. Este problema pode ser evitado usando o operador ==. Porque o compilador indicará um erro de incompatibilidade de tipo:

if(testPz.getStatus().equals(TestColor.GREEN));
if(testPz.getStatus() == TestColor.GREEN);

4. Use tipos enumerados em instruções switch

public int getDeliveryTimeInDays() {
    switch (status) {
        case ORDERED: return5;
        case READY: return2;
        case DELIVERED: return0;
    }
    return0;
}

5. Atributos, métodos e construtores de tipos de enumeração

Você pode torná-lo mais poderoso definindo atributos, métodos e construtores no tipo de enumeração.

A seguir, vamos estender o exemplo acima para realizar a transição de um estágio da pizza para outro e aprender como se livrar da instrução if e da instrução switch usadas antes:

publicclass Pizza {
 
    private PizzaStatus status;
    publicenum PizzaStatus {
        ORDERED (5){
            @Override
            public boolean isOrdered() {
                returntrue;
            }
        },
        READY (2){
            @Override
            public boolean isReady() {
                returntrue;
            }
        },
        DELIVERED (0){
            @Override
            public boolean isDelivered() {
                returntrue;
            }
        };
 
        privateint timeToDelivery;
 
        public boolean isOrdered() {returnfalse;}
 
        public boolean isReady() {returnfalse;}
 
        public boolean isDelivered(){returnfalse;}
 
        public int getTimeToDelivery() {
            return timeToDelivery;
        }
 
        PizzaStatus (int timeToDelivery) {
            this.timeToDelivery = timeToDelivery;
        }
    }
 
    public boolean isDeliverable() {
        returnthis.status.isReady();
    }
 
    public void printTimeToDeliver() {
        System.out.println("Time to delivery is " +
          this.getStatus().getTimeToDelivery());
    }
     
    // Methods that set and get the status variable.
}

O código a seguir mostra como funciona:

@Test
public void givenPizaOrder_whenReady_thenDeliverable() {
    Pizza testPz = new Pizza();
    testPz.setStatus(Pizza.PizzaStatus.READY);
    assertTrue(testPz.isDeliverable());
}

6.EnumSet e EnumMap

6.1. EnumSet

EnumSet É um tipo projetado especialmente para Set tipos enumerados  .

Em HashSetcontraste, devido ao uso de representação vetorial de bits interna, é uma representação Enum muito eficiente e compacta de um conjunto específico  de constantes.

Ele fornece uma alternativa segura para os tipos tradicionais de "sinalizadores de bits" baseados em int, permitindo-nos escrever um código conciso que é mais legível e fácil de manter.

EnumSet É uma classe abstrata, que tem duas implementações: RegularEnumSet ,, JumboEnumSetque depende do número de instâncias da seleção constantes de enumeração.

Em muitos cenários, o uso de operações de coleta de constantes de enumeração (como: subsetting, adição, exclusão containsAlle removeAlloperações em lote) EnumSeté muito adequado; se você precisar iterar todas as constantes possíveis, use-o Enum.values().

publicclass Pizza {
 
    privatestatic EnumSet<PizzaStatus> undeliveredPizzaStatuses =
      EnumSet.of(PizzaStatus.ORDERED, PizzaStatus.READY);
 
    private PizzaStatus status;
 
    publicenum PizzaStatus {
        ...
    }
 
    public boolean isDeliverable() {
        returnthis.status.isReady();
    }
 
    public void printTimeToDeliver() {
        System.out.println("Time to delivery is " +
          this.getStatus().getTimeToDelivery() + " days");
    }
 
    public static List<Pizza> getAllUndeliveredPizzas(List<Pizza> input) {
        return input.stream().filter(
          (s) -> undeliveredPizzaStatuses.contains(s.getStatus()))
            .collect(Collectors.toList());
    }
 
    public void deliver() {
        if (isDeliverable()) {
            PizzaDeliverySystemConfiguration.getInstance().getDeliveryStrategy()
              .deliver(this);
            this.setStatus(PizzaStatus.DELIVERED);
        }
    }
     
    // Methods that set and get the status variable.
}

O teste a seguir demonstra os  EnumSet recursos poderosos em determinados cenários:

@Test
public void givenPizaOrders_whenRetrievingUnDeliveredPzs_thenCorrectlyRetrieved() {
    List<Pizza> pzList = new ArrayList<>();
    Pizza pz1 = new Pizza();
    pz1.setStatus(Pizza.PizzaStatus.DELIVERED);
 
    Pizza pz2 = new Pizza();
    pz2.setStatus(Pizza.PizzaStatus.ORDERED);
 
    Pizza pz3 = new Pizza();
    pz3.setStatus(Pizza.PizzaStatus.ORDERED);
 
    Pizza pz4 = new Pizza();
    pz4.setStatus(Pizza.PizzaStatus.READY);
 
    pzList.add(pz1);
    pzList.add(pz2);
    pzList.add(pz3);
    pzList.add(pz4);
 
    List<Pizza> undeliveredPzs = Pizza.getAllUndeliveredPizzas(pzList);
    assertTrue(undeliveredPzs.size() == 3);
}

6,2 EnumMap

EnumMapÉ uma implementação de mapeamento especializada que usa constantes enum como chaves. Em comparação com sua  HashMap contraparte, é uma implementação eficiente e compacta, e é representada como uma matriz internamente:

EnumMap<Pizza.PizzaStatus, Pizza> map;

Vamos dar uma olhada rápida em um exemplo real que demonstra como usá-lo na prática:

publicstatic EnumMap<PizzaStatus, List<Pizza>>
  groupPizzaByStatus(List<Pizza> pizzaList) {
    EnumMap<PizzaStatus, List<Pizza>> pzByStatus =
      new EnumMap<PizzaStatus, List<Pizza>>(PizzaStatus.class);
     
    for (Pizza pz : pizzaList) {
        PizzaStatus status = pz.getStatus();
        if (pzByStatus.containsKey(status)) {
            pzByStatus.get(status).add(pz);
        } else {
            List<Pizza> newPzList = new ArrayList<Pizza>();
            newPzList.add(pz);
            pzByStatus.put(status, newPzList);
        }
    }
    return pzByStatus;
}

O teste a seguir demonstra os  EnumMap recursos poderosos em determinados cenários:

@Test
public void givenPizaOrders_whenGroupByStatusCalled_thenCorrectlyGrouped() {
    List<Pizza> pzList = new ArrayList<>();
    Pizza pz1 = new Pizza();
    pz1.setStatus(Pizza.PizzaStatus.DELIVERED);
 
    Pizza pz2 = new Pizza();
    pz2.setStatus(Pizza.PizzaStatus.ORDERED);
 
    Pizza pz3 = new Pizza();
    pz3.setStatus(Pizza.PizzaStatus.ORDERED);
 
    Pizza pz4 = new Pizza();
    pz4.setStatus(Pizza.PizzaStatus.READY);
 
    pzList.add(pz1);
    pzList.add(pz2);
    pzList.add(pz3);
    pzList.add(pz4);
 
    EnumMap<Pizza.PizzaStatus,List<Pizza>> map = Pizza.groupPizzaByStatus(pzList);
    assertTrue(map.get(Pizza.PizzaStatus.DELIVERED).size() == 1);
    assertTrue(map.get(Pizza.PizzaStatus.ORDERED).size() == 2);
    assertTrue(map.get(Pizza.PizzaStatus.READY).size() == 1);
}

7. Implementar alguns padrões de design por meio de enumeração

7.1 Modo Singleton

Geralmente, não é fácil implementar o padrão Singleton usando classes, e as enumerações fornecem uma maneira fácil de implementar singletons.

"Java eficaz" e "Java e padrões" recomendam fortemente esse método. Quais são os benefícios de usar esse método para implementar a enumeração?

《Java eficaz》

Esse método é semelhante em função ao método de domínio público, mas é mais conciso, fornece um mecanismo de serialização gratuitamente e evita absolutamente múltiplas instanciações, mesmo em face de serialização complexa ou ataques de reflexão. Embora esse método não tenha sido amplamente adotado, o tipo de enumeração de elemento único se tornou a melhor maneira de implementar Singleton. —- "Effective Java Chinese Edition Second Edition"

"Java e padrões"

Em "Java e Padrões", o autor escreveu que o uso da enumeração para obter o controle de instância única será mais conciso, e o mecanismo de serialização é fornecido gratuitamente, e a JVM fundamentalmente fornece garantia para evitar absolutamente múltiplas instanciações. , maneira eficiente e segura de implementar singletons.

O seguinte snippet de código mostra como usar a enumeração para implementar o modo singleton:

publicenum PizzaDeliverySystemConfiguration {
    INSTANCE;
    PizzaDeliverySystemConfiguration() {
        // Initialization configuration which involves
        // overriding defaults like delivery strategy
    }
 
    private PizzaDeliveryStrategy deliveryStrategy = PizzaDeliveryStrategy.NORMAL;
 
    public static PizzaDeliverySystemConfiguration getInstance() {
        return INSTANCE;
    }
 
    public PizzaDeliveryStrategy getDeliveryStrategy() {
        return deliveryStrategy;
    }
}

Como usá-lo? Por favor, olhe o seguinte código:

PizzaDeliveryStrategy deliveryStrategy = PizzaDeliverySystemConfiguration.getInstance().getDeliveryStrategy();

O que é  PizzaDeliverySystemConfiguration.getInstance() obtido através do singleton PizzaDeliverySystemConfiguration

7.2 Modo Estratégia

Geralmente, o padrão de estratégia é implementado por diferentes classes que implementam a mesma interface.

Isso também significa que adicionar uma nova estratégia significa adicionar uma nova classe de implementação. Usando enumerações, essa tarefa pode ser realizada facilmente, adicionar uma nova implementação significa apenas definir outra instância com uma determinada implementação.

O seguinte snippet de código mostra como usar a enumeração para implementar o padrão de estratégia:

publicenum PizzaDeliveryStrategy {
    EXPRESS {
        @Override
        public void deliver(Pizza pz) {
            System.out.println("Pizza will be delivered in express mode");
        }
    },
    NORMAL {
        @Override
        public void deliver(Pizza pz) {
            System.out.println("Pizza will be delivered in normal mode");
        }
    };
 
    public abstract void deliver(Pizza pz);
}

Para  Pizzaadicionar o seguinte método:

public void deliver() {
    if (isDeliverable()) {
        PizzaDeliverySystemConfiguration.getInstance().getDeliveryStrategy()
          .deliver(this);
        this.setStatus(PizzaStatus.DELIVERED);
    }
}

Como usá-lo? Por favor, olhe o seguinte código:

@Test
public void givenPizaOrder_whenDelivered_thenPizzaGetsDeliveredAndStatusChanges() {
    Pizza pz = new Pizza();
    pz.setStatus(Pizza.PizzaStatus.READY);
    pz.deliver();
    assertTrue(pz.getStatus() == Pizza.PizzaStatus.DELIVERED);
}

8. Java 8 e enumeração

A classe Pizza pode ser reescrita em Java 8. Você pode ver como os métodos lambda e API Stream tornam o  método getAllUndeliveredPizzas()e groupPizzaByStatus()tão conciso:

getAllUndeliveredPizzas():

public static List<Pizza> getAllUndeliveredPizzas(List<Pizza> input) {
    return input.stream().filter(
      (s) -> !deliveredPizzaStatuses.contains(s.getStatus()))
        .collect(Collectors.toList());
}

groupPizzaByStatus() :

publicstatic EnumMap<PizzaStatus, List<Pizza>>
  groupPizzaByStatus(List<Pizza> pzList) {
    EnumMap<PizzaStatus, List<Pizza>> map = pzList.stream().collect(
      Collectors.groupingBy(Pizza::getStatus,
      () -> new EnumMap<>(PizzaStatus.class), Collectors.toList()));
    return map;
}

9. Representação JSON do tipo Enum

Usando a biblioteca Jackson, JSON do tipo de enumeração pode ser expresso como POJO. O seguinte snippet de código mostra as anotações de Jackson que podem ser usadas para o mesmo propósito:

@JsonFormat(shape = JsonFormat.Shape.OBJECT)
publicenum PizzaStatus {
    ORDERED (5){
        @Override
        public boolean isOrdered() {
            returntrue;
        }
    },
    READY (2){
        @Override
        public boolean isReady() {
            returntrue;
        }
    },
    DELIVERED (0){
        @Override
        public boolean isDelivered() {
            returntrue;
        }
    };
 
    privateint timeToDelivery;
 
    public boolean isOrdered() {returnfalse;}
 
    public boolean isReady() {returnfalse;}
 
    public boolean isDelivered(){returnfalse;}
 
    @JsonProperty("timeToDelivery")
    public int getTimeToDelivery() {
        return timeToDelivery;
    }
 
    private PizzaStatus (int timeToDelivery) {
        this.timeToDelivery = timeToDelivery;
    }
}

Podemos usar Pizza e da  seguinte forma  PizzaStatus:

Pizza pz = new Pizza();
pz.setStatus(Pizza.PizzaStatus.READY);
System.out.println(Pizza.getJsonString(pz));

O status Pizza gerado é exibido no seguinte JSON:

{
  "status" : {
    "timeToDelivery" : 2,
    "ready" : true,
    "ordered" : false,
    "delivered" : false
  },
  "deliverable" : true
}

Para obter mais informações sobre serialização / desserialização JSON (incluindo personalização) de tipos de enumeração, consulte Jackson-Serialize Enumerations into JSON Objects.

10. Resumo

Neste artigo, discutimos os tipos de enumeração Java, desde o conhecimento básico até aplicativos avançados e cenários de aplicativos práticos, vamos sentir o poder da enumeração.

11. Suplemento

Como mencionamos acima, podemos torná-lo mais poderoso definindo atributos, métodos e construtores em tipos de enumeração.

Deixe-me mostrar um exemplo real. Quando chamamos o código de verificação de SMS, pode haver vários propósitos diferentes. Nós o definimos da seguinte maneira:

publicenum PinType {

    REGISTER(100000, "注册使用"),
    FORGET_PASSWORD(100001, "忘记密码使用"),
    UPDATE_PHONE_NUMBER(100002, "更新手机号码使用");

    privatefinalint code;
    privatefinal String message;

    PinType(int code, String message) {
        this.code = code;
        this.message = message;
    }

    public int getCode() {
        return code;
    }

    public String getMessage() {
        return message;
    }

    @Override
    public String toString() {
        return"PinType{" +
                "code=" + code +
                ", message='" + message + '\'' +
                '}';
    }
}

uso real:

System.out.println(PinType.FORGET_PASSWORD.getCode());
System.out.println(PinType.FORGET_PASSWORD.getMessage());
System.out.println(PinType.FORGET_PASSWORD.toString());

Resultado:

100001
忘记密码使用
PinType{code=100001, message='忘记密码使用'}

Nesse caso, será muito flexível e conveniente no uso real!

Acho que você gosta

Origin blog.csdn.net/qq_39809613/article/details/108010204
Recomendado
Clasificación