Resumir el uso de enumeraciones en el trabajo.

1. Información general

En este artículo, veremos qué son las enumeraciones de Java, qué problemas resuelven y cómo usar las enumeraciones de Java para implementar algunos patrones de diseño en la práctica.

La palabra clave enum se introdujo en java5 para indicar un tipo especial de clase, que siempre hereda la clase java.lang.Enum, y puede consultar su documentación oficial para obtener más contenido.

Las enumeraciones a menudo se comparan con constantes, probablemente porque en realidad usamos enumeraciones en grandes números para reemplazar constantes. Entonces, ¿cuáles son las ventajas de este enfoque?

Las constantes definidas de esta manera hacen que el código sea más legible, permiten verificaciones en tiempo de compilación, pregraban una lista de valores aceptables y evitan comportamientos inesperados debido a valores no válidos pasados.

El siguiente ejemplo define el estado de un pedido de pizza de tipo enumerado simple. Hay tres estados: PEDIDO, LISTO y ENTREGADO:

package shuang.kou.enumdemo.enumtest;

publicenum PizzaStatus {
    ORDERED,
    READY,
    DELIVERED;
}

En pocas palabras, evitamos definir constantes a través del código anterior, colocamos todas las constantes relacionadas con el estado del pedido de pizza en un 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 enumeración personalizado

Ahora que tenemos una comprensión básica de qué son las enumeraciones y cómo usarlas, llevemos el ejemplo anterior a un nuevo nivel definiendo algunos métodos API adicionales en las enumeraciones:

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. Utilice == para comparar los tipos enumerados

Dado que el tipo de enumeración garantiza que solo haya una instancia constante en la JVM, podemos usar con seguridad el operador "==" para comparar dos variables, como se muestra en el ejemplo anterior; además, el operador "==" puede proporcionar compile -Tiempo y seguridad en tiempo de ejecución.

Primero, echemos un vistazo a la seguridad en tiempo de ejecución en el siguiente fragmento de código, donde el operador "==" se usa para comparar estados, y si ambos valores son nulos, NullPointerException no se lanzará. Por el contrario, si se usa el método equals, se lanzará una NullPointerException:

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

Para la seguridad en tiempo de compilación, veamos otro ejemplo. Se comparan dos tipos de enumeración diferentes. Se determina que el resultado de la comparación utilizando el método de igualdad es verdadero, porque el valor de enumeración del método getStatus es coherente con el valor de enumeración de otro type, pero lógicamente debería ser falso. Este problema se puede evitar utilizando el operador ==. Porque el compilador indicará un error de incompatibilidad de tipos:

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

4. Utilice tipos enumerados en declaraciones de cambio

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

5. Atributos, métodos y constructores de tipos de enumeración

Puede hacerlo más poderoso definiendo atributos, métodos y constructores en el tipo de enumeración.

A continuación, extendamos el ejemplo anterior para realizar la transición de una etapa de pizza a otra, y aprendamos cómo deshacerse de la declaración if y la declaración switch que se usaban 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.
}

El siguiente código muestra cómo funciona:

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

6.EnumSet y EnumMap

6.1. EnumSet

EnumSet Es un tipo especialmente diseñado para Set tipos enumerados  .

Por el HashSetcontrario, debido al uso de la representación de vector de bits interno, es una representación Enum muy eficiente y compacta de un conjunto específico  de constantes.

Proporciona una alternativa segura de tipos a los tradicionales "indicadores de bits" basados ​​en int, lo que nos permite escribir código conciso que es más legible y fácil de mantener.

EnumSet Es una clase abstracta, que tiene dos implementaciones: RegularEnumSet ,, JumboEnumSetque depende del número de casos de la selección constantes de enumeración.

En muchos escenarios, el uso de operaciones de recopilación de constantes de enumeración (tales como: operaciones de subconjunto, adición, eliminación containsAlly por removeAlllotes) EnumSetes muy adecuado; si necesita iterar todas las constantes posibles, úsela 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.
}

La siguiente prueba demuestra las  EnumSet potentes funciones en determinados escenarios:

@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

EnumMapEs una implementación de mapeo especializada que usa constantes enum como claves. En comparación con su  HashMap contraparte, es una implementación eficiente y compacta, y se representa internamente como una matriz:

EnumMap<Pizza.PizzaStatus, Pizza> map;

Echemos un vistazo rápido a un ejemplo real que demuestra cómo usarlo en la práctica:

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;
}

La siguiente prueba demuestra las  EnumMap potentes funciones en determinados escenarios:

@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 algunos patrones de diseño mediante enumeración.

7.1 Modo singleton

Generalmente, no es fácil implementar el patrón Singleton usando clases y las enumeraciones proporcionan una manera fácil de implementar singletons.

"Effective Java" y "Java and Patterns" recomiendan encarecidamente este método. ¿Cuáles son los beneficios de utilizar este método para implementar la enumeración?

《Java efectivo》

Este método es similar en función al método de dominio público, pero es más conciso, proporciona un mecanismo de serialización de forma gratuita y evita absolutamente la creación de instancias múltiples, incluso frente a complejos ataques de serialización o reflexión. Aunque este método no se ha adoptado ampliamente, el tipo de enumeración de un solo elemento se ha convertido en la mejor manera de implementar Singleton. —- "Segunda edición de la edición en chino de Java efectivo"

"Java y patrones"

En "Java and Patterns", el autor escribió que el uso de la enumeración para lograr el control de una sola instancia será más conciso, y el mecanismo de serialización se proporciona de forma gratuita, y la JVM proporciona fundamentalmente una garantía para evitar absolutamente múltiples instancias. Una versión más concisa , forma eficiente y segura de implementar singletons.

El siguiente fragmento de código muestra cómo usar la enumeración para implementar el 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;
    }
}

¿Cómo usarlo? Mire el siguiente código:

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

Lo que se  PizzaDeliverySystemConfiguration.getInstance() obtiene mediante es singleton PizzaDeliverySystemConfiguration

7.2 Modo de estrategia

Generalmente, el patrón de estrategia es implementado por diferentes clases que implementan la misma interfaz.

Esto también significa que agregar una nueva estrategia significa agregar una nueva clase de implementación. Usando enumeraciones, esta tarea se puede hacer fácilmente, agregar una nueva implementación significa solo definir otra instancia con una implementación determinada.

El siguiente fragmento de código muestra cómo utilizar la enumeración para implementar el patrón de estrategia:

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  Pizzaagregar el siguiente método:

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

¿Cómo usarlo? Mire el siguiente 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 y enumeración

La clase Pizza se puede reescribir en Java 8. Puede ver cómo los métodos lambda y Stream API hacen que el  método getAllUndeliveredPizzas()y sea groupPizzaByStatus()tan 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. Representación JSON del tipo Enum

Usando la biblioteca de Jackson, JSON del tipo de enumeración se puede expresar como POJO. El siguiente fragmento de código muestra las anotaciones de Jackson que se pueden usar para el mismo 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 y de la  siguiente manera  PizzaStatus:

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

El estado de pizza generado se muestra en el siguiente JSON:

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

Para obtener más información sobre la serialización / deserialización JSON (incluida la personalización) de los tipos de enumeración, consulte Jackson-Serializar enumeraciones en objetos JSON.

10. Resumen

En este artículo, discutimos los tipos de enumeración de Java, desde el conocimiento básico hasta las aplicaciones avanzadas y los escenarios de aplicación práctica, permítanos sentir el poder de la enumeración.

11. Suplemento

Como mencionamos anteriormente, podemos hacerlo más poderoso definiendo atributos, métodos y constructores en tipos de enumeración.

Permítanme mostrarles un ejemplo real. Cuando llamamos al código de verificación por SMS, puede haber varios propósitos diferentes. Lo definimos de la siguiente manera:

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 actual:

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

Producción:

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

En este caso, será muy flexible y conveniente en el uso real.

Supongo que te gusta

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