Enumeraciones de Java que puede que no entiendas

Las enumeraciones también son una vieja charla en Java. Siempre que encontremos un conjunto de datos que necesite ser enumerado, naturalmente usaremos tipos de enumeración:

public enum Color { ROJO, VERDE, AZUL, AMARILLO;

public static void main(String[] args) {
    Color red = Color.RED;
    Color redAnother = Color.RED;
    Color blue = Color.BLUE;

    System.out.println(red.equals(redAnother)); // true
    System.out.println(red.equals(blue)); // false
}

}
Por supuesto, lo que vamos a discutir hoy no es la sintaxis básica de enum en java. Este tema entrará en la esencia de enum y discutirá algunos usos de alto nivel. Este artículo está basado en Oracle JDK 14.0.2 y jad v1.5.8e (debido a que jad no se ha actualizado durante mucho tiempo, el soporte para la nueva versión de jdk no es muy completo, pero basta con analizar la enumeración y la interfaz).

El secreto detrás de los valores de enumeración personalizados El
valor predeterminado de una enumeración es un valor que aumenta desde 0, que suele ser suficiente. Sin embargo, Java también nos permite personalizar el valor de la enumeración, por ejemplo:

// No solo queremos usar la orientación en inglés, sino que también queremos obtener el nombre localizado correspondiente (aquí en chino)
enum Dirección { EAST (“东”), WEST (“西”), NORTH (“北”), SOUTH ( "sur");



private final String name;

// 注意是private
private Direction(String name) {
    this.name = name;
}

public String getName() {
    return this.name;
}

}

Prueba de clase pública { public static void main (String [] args) { for (var v: Direction.values ​​()) { System.out.println (v.toString () + “->” + v.getName ()) ; } } } Compila y ejecuta el programa, obtendrás el siguiente resultado:






ESTE–> Este
OESTE–> Oeste
NORTE–> Norte
SUR–> Sur.
Este es el final de muchos tutoriales. Hasta ahora, ¿cuál es el efecto de los paréntesis después del valor de enumeración? ¿Por qué el constructor requiere una modificación privada? No se da ninguna explicación. Sin embargo, comprenderlos es la premisa para estudiar más a fondo el uso avanzado de la enumeración.

Pero no importa, podemos explorarlo nosotros mismos, por ejemplo, mirar el código descompilado y explorar la forma en que el compilador maneja los tipos de enumeración. Aquí usaremos jad, y nos referiremos a otros artículos excelentes en el parque para tutoriales de uso específicos. Este artículo no entrará en detalles, veremos directamente los resultados después de la descompilación:

La dirección final de la clase extiende Enum
{

/* 省略部分无关紧要的方法 */

private Direction(String s, int i, String s1)
{
    super(s, i);
    name = s1;
}

public String getName() // 这是我们自定义的getter
{
    return name;
}

public static final Direction EAST;
public static final Direction WEST;
public static final Direction NORTH;
public static final Direction SOUTH;
private final String name;
// 省略不重要的部分字段

static 
{
    EAST = new Direction("EAST", 0, "\u4E1C");
    WEST = new Direction("WEST", 1, "\u897F");
    NORTH = new Direction("NORTH", 2, "\u5317");
    SOUTH = new Direction("SOUTH", 3, "\u5357");
    // 省略部分字段的初始化
}

} En
primer lugar, vemos que nuestra enumeración es una clase y, en segundo lugar, hereda de java.lang.Enum (lo que significa que enum no puede especificar explícitamente la clase base), y llamamos a la estructura de su clase padre en el constructor de Direction Función, al leer el documento, podemos ver que el constructor de java.lang.Enum está protegido, lo que significa que los usuarios fuera del paquete java.lang no pueden llamar a este constructor. Al mismo tiempo, el documento también señaló que el compilador llama automáticamente al constructor. Por lo tanto, el constructor de enumeración definido por nosotros no se puede llamar normalmente y solo el compilador puede usarlo para inicializar miembros de enumeración. Dado que el usuario no puede llamarlo, Java simplemente no permite que protected y public (permiso predeterminado y privado) modifiquen el constructor del tipo de enumeración personalizado para evitar un mal uso.

Además, nuestro constructor personalizado es realmente sintetizado por el compilador. Además de los parámetros personalizados, están los nombres de cadena de los miembros de enumeración y un número de serie que comienza desde 0 (se puede obtener mediante el método ordinal). Los dos primeros parámetros se compilan El dispositivo se agregará automáticamente por nosotros, y el parámetro personalizado se pasa al constructor en función del valor entre paréntesis después del miembro de enumeración que damos, en resumen:

ESTE ("Este"),
OESTE ("西"),
NORTE ("北"),
SUR ("南");

// Convertido a (los caracteres Unicode se transcodifican)
EAST = new Direction ("EAST", 0, "\ u4E1C");
WEST = new Direction ("WEST", 1, "\ u897F");
NORTH = new Direction ( "NORTE", 2, "\ u5317");
SUR = nueva dirección ("SUR", 3, "\ u5357");
Si necesito más campos, así:

public enum Planet { // Con dos valores personalizados MERCURY (3.303e + 23, 2.4397e6), VENUS (4.869e + 24, 6.0518e6), EARTH (5.976e + 24, 6.37814e6), MARS (6.421e +23, 3.3972e6), JÚPITER (1.9e + 27, 7.1492e7), SATURNO (5.688e + 26, 6.0268e7), URANO (8.686e + 25, 2.5559e7), NEPTUNO (1.024e + 26, 2.4746e7) ;








// 保存自定义值的字段,不使用final也可以,但枚举值一般不应该发生改变
private final double mass;   // in kilograms
private final double radius; // in meters
// 在这里使用default的权限控制,即package-private
Planet(double mass, double radius) {
    this.mass = mass;
    this.radius = radius;
}
public double mass() { return mass; }
public double radius() { return radius; }

}
Este es el secreto detrás de los valores de enumeración personalizados.

Hasta ahora nuestras preguntas casi han sido respondidas, pero una observación cuidadosa revelará que los miembros de nuestra enumeración son todos _ campos estáticos_ de Direction. Por lo tanto, no podemos usar estos miembros de enumeración como tipos:

public void work (Direction.EAST e) { // Esto no se puede compilar } El campo estático se comprende bien, porque necesitamos usar el nombre de clase + el nombre del miembro de enumeración Direction.WEST para hacer referencia directamente, pero ¿por qué debería ser el tipo de campo Direction? ?


No se preocupe, la respuesta se revelará en la siguiente sección.

Agregar métodos abstractos a las enumeraciones
Esta sección parece ridícula Los métodos abstractos parecen ser incompatibles con la enumeración. Pero piénselo detenidamente. En la sección anterior, hemos agregado un método getter member a la enumeración. Esto muestra que también podemos agregar otros métodos a la enumeración para personalizar el comportamiento del tipo de enumeración. El Planet en la sección anterior es un ejemplo. Podemos agregar el cálculo de la gravedad y masa de cualquier objeto en la superficie de un planeta:

public enum Planet {

/* 定义枚举成员和初始化的相关重复代码,此处不再重复 */

private double mass() { return mass; }
private double radius() { return radius; }

// universal gravitational constant  (m3 kg-1 s-2)
public static final double G = 6.67300E-11;

double surfaceGravity() {
    return G * mass / (radius * radius);
}
double surfaceWeight(double otherMass) {
    return otherMass * surfaceGravity();
}
public static void main(String[] args) {
    if (args.length != 1) {
        System.err.println("Usage: java Planet <earth_weight>");
        System.exit(-1);
    }
    double earthWeight = Double.parseDouble(args[0]);
    double mass = earthWeight/EARTH.surfaceGravity();
    for (Planet p : Planet.values())
       System.out.printf("Your weight on %s is %f%n",
                         p, p.surfaceWeight(mass));
}

} Los
resultados de ejecución son los siguientes:

$ java Planet.java 70

Su peso sobre el mercurio es 26.443033
su peso de Venus es 63.349937
su peso en la tierra es 70.000000
su peso en Marte es 26.511603
su peso sobre Júpiter es 177.139027
su peso en Saturno IS 74.621088
su peso sobre Urano es 63.358904
su peso sobre Neptuno es 79.682965
dado que se Personalizar el comportamiento de toda la enumeración, ¿significa eso que podemos definir el comportamiento de los miembros de la enumeración por separado? Después de todo, el método se llama en última instancia desde el valor del miembro de enumeración.

La respuesta es Sí. ¿Recuerda cómo el compilador manejó los miembros de la enumeración en la última parte de la sección anterior?

ESTE = nueva dirección ("ESTE", 0, "\ u4E1C");
OESTE = nueva dirección ("OESTE", 1, "\ u897F");
NORTE = nueva dirección ("NORTE", 2, "\ u5317") ;
SOUTH = new Direction ("SOUTH", 3, "\ u5357");
Sí, el miembro de enumeración en sí también es una instancia del objeto enum. Y aunque estos miembros de enumeración son de tipo Dirección, en realidad pueden hacer referencia a tipos derivados de Dirección.

Supongamos que tenemos una enumeración del tipo de color, y para cada miembro de la enumeración tenemos un método de impresión personalizado para imprimir información diferente:

enum Color { RED { // No se preocupe por la sintaxis por ahora, lo explicaré más adelante @Override public void print () { // Genere una cadena de color en Linux System.out.println ("\ u001B [1; 31m Esto es rojo text \ u001B [0m "); } }, AZUL { @Override public void print () { System.out.println (" \ u001B [1; 34m Este es el texto azul \ u001B [0m "); } }, VERDE { @Override public void print () { System.out.println ("\ u001B [1; 32m Esto es texto verde \ u001B [0m"); } };



















// 枚举成员必须要覆写的抽象方法
public abstract void print();

}

prueba de clase pública { public static void main (String [] args) { for (var v: Color.values ​​()) { v.print (); } } } Los resultados de ejecución son los siguientes:






Para conocer el principio, todavía tenemos que usar jad, que es el contenido procesado de Color.class:

// se convierte en una clase abstracta clase
abstracta Color amplía Enum
{ // constructor color privado (String s, int i) { super (s, i); }




public abstract void print();

public static final Color RED;
public static final Color BLUE;
public static final Color GREEN;

static 
{
    // 重点从这开始
    RED = new Color("RED", 0) {

        public void print()
        {
            System.out.println("\033[1;31m This is red text \033[0m");
        }

    };
    BLUE = new Color("BLUE", 1) {

        public void print()
        {
            System.out.println("\033[1;34m This is blue text \033[0m");
        }

    };
    GREEN = new Color("GREEN", 2) {

        public void print()
        {
            System.out.println("\033[1;32m This is green text \033[0m");
        }

    };
}

} Los
lectores cuidadosos probablemente lo hayan descubierto, ¿no es esta _clase interna anónima_? Así es, nuestro tipo de enumeración se ha convertido en realidad en una clase abstracta esta vez, y los miembros de la enumeración son clases internas anónimas que heredan de Color e implementan métodos abstractos. Por tanto, las llaves que marcamos con comentarios al principio se pueden entender como el cuerpo de la clase anónima. Sin embargo, debe tenerse en cuenta que aunque aquí se usa explícitamente new para crear una clase interna anónima, el compilador sigue llamando al constructor.

¿Qué sucede si desea agregar datos enumerados personalizados? Puedes hacerlo:

enum Color { RED (31) { @Override public void print () { System.out.println ("\ u001B [1; 31m Este es texto rojo \ u001B [0m"); } }, AZUL (34) { @Override public void print () { System.out.println ("\ u001B [1; 34m Este es el texto azul \ u001B [0m"); } }, VERDE (32) { @Override public void print () { System.out.println ("\ u001B [1; 32m Este es el texto verde \ u001B [0m"); } };

















// color code
private final int colorCode;
private Color(int code) {
    colorCode = code;
}
public int getColorCode() {
    return colorCode;
}

public abstract void print();

}
Echemos un vistazo al código compilado. Debido a limitaciones de espacio, solo conservé la parte importante:

clase abstracta Color amplía Enum
{

/* 大量省略代码 */

private Color(String s, int i, int j)
{
    super(s, i);
    colorCode = j;
}

public abstract void print();

public static final Color RED;
public static final Color BLUE;
public static final Color GREEN;
private final int colorCode;

static 
{
    // 参数传递给了构造函数
    RED = new Color("RED", 0, 31) {

        public void print()
        {
            System.out.println("\033[1;31m This is red text \033[0m");
        }

    };
    BLUE = new Color("BLUE", 1, 34) {

        public void print()
        {
            System.out.println("\033[1;34m This is blue text \033[0m");
        }

    };
    GREEN = new Color("GREEN", 2, 32) {

        public void print()
        {
            System.out.println("\033[1;32m This is green text \033[0m");
        }

    };
}

}
En resumen, para un tipo de enumeración, suele existir el siguiente formato:

[public] enum NAME [implementa XXX,…] { VALUE1 [(Datos personalizados, el formato es el mismo que la lista de parámetros de la función del constructor personalizado)] [{ // Puede anular o agregar nuevos métodos }], …, VALUEN [(…)] [{ // anulaciones o métodos }];








[存储各种自定义数据的字段,最好用final修饰]
[
    // 自定义构造函数
    [private] NAME(和枚举成员中给出的圆括号内的内容一致) { /* 设置数据字段 */ }
]

[定义抽象方法或者重写object/Enum的方法或是添加普通类方法]

}
La parte encerrada por [] en el formato dado se puede omitir.

Enumeración e interfaz
Al final de la sección anterior, vimos que enum realmente puede implementar una interfaz (después de todo, es esencialmente una clase), por lo que el ejemplo de la sección anterior se puede escribir así:

interfaz Impresora { void print (); }

enum Color implementa Impresora { ROJO { @Override public void print () { System.out.println ("\ u001B [1; 31m Esto es texto rojo \ u001B [0m"); } }, AZUL { @Override public void print ( ) { System.out.println ("\ u001B [1; 34m Este es el texto azul \ u001B [0m"); } }, VERDE { @Override public void print () { System.out.println ("\ u001B [1 ; 32m Esto es texto verde \ u001B [0m "); } }; } Yo personalmente prefiero el segundo método, porque enum es principalmente una colección de datos, y el comportamiento / patrón exhibido por los datos se describe usando la interfaz tanto como sea posible.



















Además, enum también se puede definir en iinterface. Supongamos que tenemos una enumeración que representa de lunes a domingo, y se proporciona un método isRestDay para determinar si se puede descansar en la fecha actual (por ejemplo, algunas personas tienen fines de semana y otras tienen un solo día libre, y algunas personas descansan el lunes o viernes). Los tipos de personas tendrán diferentes respuestas al día de la semana, por lo que es más apropiado abstraerlo como una interfaz:

interfaz Relajable { enum Semanal { Lun, Mar, Mié, Jue, Vie, Sáb, Dom }


boolean isRestDay(Relaxable.Weekly day);

}

class PersonA implementa Relaxable { @Override public boolean isRestDay (Relaxable.Weekly day) { return day.equals (Relaxable.Weekly.Sat) || day.equals (Relaxable.Weekly.Sun); } }




class PersonB implementa Relaxable { @Override public boolean isRestDay (Relaxable.Weekly day) { return day.equals (Relaxable.Weekly.Sun); } }




public class Relax { public static void main (String [] args) { var a = new PersonA (); var b = new PersonB (); var day = Relaxable.Weekly.Sat; System.out.println (a.isRestDay ( day)); // true System.out.println (b.isRestDay (día)); // false } } PersonA tiene un buen fin de semana, ¡y la pobre PersonB tiene que trabajar horas extras el sábado! Utilice jad para ver el código producido:








interfaz Relajable
{ público estático final clase Semanal extiende Enum {

    /* 省略了部分代码 */

    public static final Weekly Mon;
    public static final Weekly Tue;
    public static final Weekly Wed;
    public static final Weekly Thu;
    public static final Weekly Fri;
    public static final Weekly Sat;
    public static final Weekly Sun;

    static 
    {
        Mon = new Weekly("Mon", 0);
        Tue = new Weekly("Tue", 1);
        Wed = new Weekly("Wed", 2);
        Thu = new Weekly("Thu", 3);
        Fri = new Weekly("Fri", 4);
        Sat = new Weekly("Sat", 5);
        Sun = new Weekly("Sun", 6);
    }

    private Weekly(String s, int i)
    {
        super(s, i);
    }
}

public abstract boolean isRestDay(Weekly weekly);

}
Evaluación de Amazon www.yisuping.com

Supongo que te gusta

Origin blog.csdn.net/weixin_45032957/article/details/108511126
Recomendado
Clasificación