[JavaSE] Comprehensive interpretation of Java enumeration (enum)

1. Definition of enumeration

Enhancements in Java SE 5.0

  • Typesafe Enums - This flexible object-oriented enumerated type facility allows you to create enumerated types with arbitrary methods and fields. It provides all the benefits of the Typesafe Enum pattern (“Effective Java,” Item 21) without the verbosity and the error-proneness. (JSR 201)

Google translates as follows:
Typesafe Enums - This flexible object-oriented enum type tool allows you to create enum types with arbitrary methods and fields. It offers all the benefits of the Typesafe Enum pattern ("Effective Java", Item 21), without the verbosity and error-prone problems. (JSR 201)

An enumeration type (enum type) is a special reference type provided by the Java 5 version: a special class defined with the keyword enum.
Enumerations, like classes (Class), can contain fields, constructors, and methods, and can also implement interfaces.

Second, the characteristics of the enumeration

2.1. Legibility/type safety

Before there was an enumerated type, fixed constants representing the seasons of the year were generally defined using int or String constants. like:

public  static  final  int  SPRING = 1;
public  static  final  int  SUMMER = 2;
public  static  final  int  AUTUMN = 3;
public  static  final  int  WINTER = 4;
// 或者
public  static  final  String  SPRING = "spring";

This method is called int枚举模式, String枚举模式and there are many shortcomings in this approach:

1. Int is 易读性poor. Although constant variable names can be used to distinguish, but using int directly without constants will not report an error.
2. The legibility of String is no problem, but the comparison depends on the comparison of strings, which has 性能problems
. 3. Both have 类型安全性problems

The so-called type safety means that as long as the type matches, the code will not report an error even if the value is wrong, but the running result does not conform to the business logic.
As above: the four seasons of the year are defined as 1234, and the program uses non-existent illegal values ​​such as 456, or does not use the defined constants, but SPRINGhard-coded spelling springand wrongly written sprnig, the compilation will pass normally, but the runtime will fail because it does not conform to the agreement And there is a business logic error.

A simple enum is defined as follows:

public enum SeasonEnum {
    
     
    SPRING, SUMMER, AUTUMN, WINTER
}

Enumeration types are guaranteed to be compile-time 类型安全性, not enumeration-specific 编译时就会报错.

2.2. Singleton

insert image description here

As shown in the figure above, enumeration guarantees its singleton characteristics through three mechanisms:

1. The enumeration method cannot be cloned
2. The enumeration cannot be instantiated by reflection
3. The enumeration cannot be created by deserialization

Third, the use of enumeration

3.1, constant/enumeration comparison/switch

The most basic usage is to only declare constants for enumeration instances

public enum FruitsEnum {
    
    
    /** 苹果 */
    APPLE,
    /** 香蕉 */
    BANANA,
    /** 橙子 */
    ORANGES,
    /** 葡萄 */
    GRAPES,
    /** 未知 */
    UNKNOWN
}
/**
 * 基本使用
 * <pre>
 * 1、枚举常量测试
 * 2、枚举比较测试
 * 3、switch测试
 * </pre>
 *
 * @author gmlee
 * @date 2023-02-25
 */
public class FruitsEnumTest {
    
    

    @Test
    public void constantsTest() {
    
    
        System.out.println(FruitsEnum.APPLE);
        System.out.println(FruitsEnum.APPLE.name());

        Assertions.assertEquals("APPLE", FruitsEnum.APPLE.name());

        // 输出:
        // APPLE
        // APPLE
    }


    /**
     * <li>
     * 枚举本身就是单例的,枚举间的比较用 == 还是 equals效果是一样的,equals底层就是用 == 来比较的
     * <li>
     * 枚举 compareTo()方法比较的是 ordinal 的大小
     * <pre>
     *     public final int compareTo(E o) {
     *         Enum<?> other = (Enum<?>)o;
     *         Enum<E> self = this;
     *         if (self.getClass() != other.getClass() && // optimization
     *             self.getDeclaringClass() != other.getDeclaringClass())
     *             throw new ClassCastException();
     *         return self.ordinal - other.ordinal;
     *     }
     */
    @Test
    public void compareTest() {
    
    
        if (FruitsEnum.APPLE == FruitsEnum.APPLE) {
    
    
            System.out.println("true....");
        }

        if (FruitsEnum.APPLE.equals(FruitsEnum.APPLE)) {
    
    
            System.out.println("true....");
        }

        // ordinal 从0开始自增
        System.out.println("APPLE.ordinal(): " + FruitsEnum.APPLE.ordinal());
        System.out.println("GRAPES.ordinal(): " + FruitsEnum.GRAPES.ordinal());
        Assertions.assertEquals(0, FruitsEnum.APPLE.ordinal());
        Assertions.assertEquals(3, FruitsEnum.GRAPES.ordinal());

        Assertions.assertEquals(-3, FruitsEnum.APPLE.compareTo(FruitsEnum.GRAPES));
        // 输出:
        // true....
        // true....
        // APPLE.ordinal(): 0
        // GRAPES.ordinal(): 3
    }


    @Test
    public void switchTest() {
    
    
        final String apple = fruitSwitch(FruitsEnum.APPLE);
        Assertions.assertEquals("苹果!", apple);

        final String unknown = fruitSwitch(FruitsEnum.UNKNOWN_FRUITS);
        Assertions.assertEquals("未知水果!", unknown);

        // 输出:
        // 水果是:苹果!
        // 水果是:未知水果!
    }


    /**
     * Switch测试
     */
    private String fruitSwitch(FruitsEnum fruit) {
    
    
        String name = "";
        switch (fruit) {
    
    
            case APPLE:
                name = "苹果!";
                break;
            case BANANA:
                name = "香蕉!";
                break;
            case ORANGES:
                name = "橙子!";
                break;
            case GRAPES:
                name = "葡萄!";
                break;
            case UNKNOWN_FRUITS:
            default:
                name = "未知水果!";
        }
        System.out.println("水果是:" + name);
        return name;
    }

}

3.2. Member attributes and methods

/**
 * 具有 自定义构造器、属性 的枚举常量,属性查询方法
 * <pre>
 * 《Java开发手册(黄山版)》阿里规约:
 * (九) 注释规约
 * 6.【强制】枚举 enum(括号内)的属性字段必须是私有且不可变。
 * @author gmlee
 * @date 2023-02-25
 */
@Getter
public enum LanguageEnum {
    
    

    JAVA(1, "java", "hello java"),
    PYTHON(2, "python", "hello python"),
    GO(3, "go", "hello golang"),
    PHP(4, "php", "PHP是世界上最美的语言!"),
    UNKNOWN(-1, "unknown", "未知的语言!");

    /**
     * 自定义属性字段
     */
    private final Integer code;

    private final String name;

    private final String desc;

    /**
     * 枚举构造器
     * <pre>
     *    1、同 Class,不显示声明,则自动隐士声明一个无参构造器
     *    2、不同于Class,枚举的构造器 访问修饰符缺省为 private
     *    3、枚举的构造器若声明为 public、protected 时,编译报错
     * </pre>
     */
    LanguageEnum(Integer code, String name, String desc) {
    
    
        this.code = code;
        this.name = name;
        this.desc = desc;
    }

    /**
     * 静态方法,获取枚举方式一:遍历
     */
    public static LanguageEnum getLanguageByCode(Integer code) {
    
    
        for (LanguageEnum value : LanguageEnum.values()) {
    
    
            if (value.code.equals(code)) {
    
    
                return value;
            }
        }
        return UNKNOWN;
    }


    /**
     * 获取枚举方式二:Map
     */
    private static final Map<String, LanguageEnum> LANGUAGE_ENUM_MAP = new HashMap<>();

    static {
    
    
        for (LanguageEnum value : LanguageEnum.values()) {
    
    
            LANGUAGE_ENUM_MAP.put(value.getName(), value);
        }
    }

    public static LanguageEnum getLanguageByName(String name) {
    
    
        return LANGUAGE_ENUM_MAP.get(name);
    }

}
/**
 * 枚举 属性/静态方法测试
 *
 * @author gmlee
 * @date 2023-02-25
 */
public class LanguageEnumTest {
    
    

    @Test
    public void memberTest() {
    
    
        // JAVA(1, "java", "hello java")
        Assertions.assertEquals(1, LanguageEnum.JAVA.getCode());
        Assertions.assertEquals("java", LanguageEnum.JAVA.getName());
        Assertions.assertEquals("hello java", LanguageEnum.JAVA.getDesc());
    }

    @Test
    public void staticMethodTest() {
    
    
        // JAVA(1, "java", "hello java")
        Assertions.assertEquals(LanguageEnum.JAVA, LanguageEnum.getLanguageByCode(1));
        Assertions.assertEquals(LanguageEnum.JAVA, LanguageEnum.getLanguageByName("java"));

        Assertions.assertNull(LanguageEnum.getLanguageByName("xxx"));
    }

}

3.3. Abstract method

"Effective Java Chinese Edition (Original Book 3rd Edition)" is called: 特定于常量的方法实现(constant-specific method implementation ), that is, strategy enumeration (strategy enum)

/**
 * 特定于常量的方法实现(constant-specific method  implementation )
 *
 * @author gmlee
 * @date 2023-02-25
 */
@Getter
@AllArgsConstructor
public enum SeasonEnum {
    
    
    SPRING("春") {
    
    
        @Override
        public void printDesc() {
    
    
            System.out.println(this.getName() + ": " + "沾衣欲湿杏花雨,吹面不寒杨柳风!");
        }
    },
    SUMMER("夏") {
    
    
        @Override
        public void printDesc() {
    
    
            System.out.println(this.getName() + ": " + "接天莲叶无穷碧,映日荷花别样红!");
        }
    },
    AUTUMN("秋") {
    
    
        @Override
        public void printDesc() {
    
    
            System.out.println(this.getName() + ": " + "落霞与孤鹜齐飞,秋水共长天一色!");
        }
    },
    WINTER("冬") {
    
    
        @Override
        public void printDesc() {
    
    
            System.out.println(this.getName() + ": " + "千里黄云白日曛,北风吹雁雪纷纷!");
        }
    };

    /**
     * 名称
     */
    private final String name;

    /**
     * 抽象方法
     * <pre>
     *     1、枚举中包含抽象方法会编译报错,除非满足一下条件:
     *     1.1、枚举中至少有一个枚举常量,如本枚举 SPRING("春"),
     *     1.2、所有的枚举常量都在Body体中提供该抽象方法的实现
     * </pre>
     */
    public abstract void printDesc();

}
/**
 * 枚举 抽象方法测试
 *
 * @author gmlee
 * @date 2023-02-25
 */
public class SeasonEnumTest {
    
    

    @Test
    public void abstractMethodTest() {
    
    
        Assertions.assertEquals("春", SeasonEnum.SPRING.getName());

        for (SeasonEnum value : SeasonEnum.values()) {
    
    
            value.printDesc();
        }
    }

}

3.4, implement the interface

public interface IWork {
    
    

    String work();

}

method one

/**
 * 实现接口,同 Class,实现接口,重写接口方法
 *
 * @author gmlee
 * @date 2023-02-25
 */
@Getter
@AllArgsConstructor
public enum Week1Enum implements IWork {
    
    
    MONDAY(1, "周一"),
    TUESDAY(2, "周二"),
    WEDNESDAYS(3, "周三"),
    THURSDAY(4, "周四"),
    FRIDAY(5, "周五"),
    SATURDAY(6, "周六"),
    SUNDAY(7, "周日");

    private final Integer dayOfWeek;

    private final String name;

    @Override
    public String work() {
    
    
        return this.name + ": Week, do work.....";
    }


}

way two

/**
 * 实现接口,通过枚举常量Body体来实现该接口的方法
 *
 * @author gmlee
 * @date 2023-02-25
 */
@Getter
@AllArgsConstructor
public enum Week2Enum implements IWork {
    
    
    MONDAY(1, "周一") {
    
    
        @Override
        public String work() {
    
    
            return getName() + ": Week1, 一帆风顺!";
        }
    },
    TUESDAY(2, "周二") {
    
    
        @Override
        public String work() {
    
    
            return getName() + ": Week1, 两全其美!";
        }
    },
    WEDNESDAYS(3, "周三") {
    
    
        @Override
        public String work() {
    
    
            return getName() + ": Week1, 三阳开泰!";
        }
    },
    THURSDAY(4, "周四") {
    
    
        @Override
        public String work() {
    
    
            return getName() + ": Week1, 四季发财!";
        }
    },
    FRIDAY(5, "周五") {
    
    
        @Override
        public String work() {
    
    
            return getName() + ": Week1, 五福临门!";
        }
    },
    SATURDAY(6, "周六") {
    
    
        @Override
        public String work() {
    
    
            return getName() + ": Week1, 六六大顺!";
        }
    },
    SUNDAY(7, "周日") {
    
    
        @Override
        public String work() {
    
    
            return getName() + ": Week1, 七星高照!";
        }
    };

    private final Integer dayOfWeek;

    private final String name;

}

unit test

/**
 * 枚举 接口实现测试
 *
 * @author gmlee
 * @date 2023-02-25
 */
public class WeekEnumTest {
    
    

    @Test
    public void interfaceTest() {
    
    
        System.out.println("↓============ Week  ============↓");
        for (Week1Enum value : Week1Enum.values()) {
    
    
            System.out.println(value.work());
        }

        System.out.println("↓============ Week1 ============↓");
        for (Week2Enum value : Week2Enum.values()) {
    
    
            System.out.println(value.work());
        }
    }
        
    // 输出:
    // ↓============ Week  ============↓
    // 周一: Week, do work.....
    // 周二: Week, do work.....
    // 周三: Week, do work.....
    // 周四: Week, do work.....
    // 周五: Week, do work.....
    // 周六: Week, do work.....
    // 周日: Week, do work.....
    // ↓============ Week1 ============↓
    // 周一: Week1, 一帆风顺!
    // 周二: Week1, 两全其美!
    // 周三: Week1, 三阳开泰!
    // 周四: Week1, 四季发财!
    // 周五: Week1, 五福临门!
    // 周六: Week1, 六六大顺!
    // 周日: Week1, 七星高照!
}

4. Enumeration usage specification - Ali "Java Development Manual (Huangshan Edition)"

1. Programming protocol

(1) Naming style

18. [Reference] Add Enum suffix to the enumeration class name, enumeration member names need to be all capitalized, and words should be separated by underscores.
Explanation: Enumeration is actually a special constant class, and the constructor is forced to be private by default.
Positive example: Member name of the enumeration name ProcessStatusEnum: SUCCESS / UNKNOWN_REASON

(2) Constant definition

6. [Recommendation] If the variable value only changes within a fixed range, use the enum type to define it.
Note: If there is an extended attribute other than the name, the enum type should be used. The number in the positive example below is the extended information, indicating the season of the year.
Positive example:

public enum SeasonEnum {
    
    
    SPRING(1), SUMMER(2), AUTUMN(3), WINTER(4);
    private int seq;
    SeasonEnum(int seq) {
    
    
        this.seq = seq;
    }
    public int getSeq() {
    
    
        return seq;
    }
}

(5) Date and time

7. [Recommendation] Use the enumeration value to refer to the month. If numbers are used, please note that the value of month in date-related classes such as Date and Calendar
ranges from 0 to 11.
Explanation: Refer to the JDK native annotation, Month value is 0-based. eg, 0 for January.
Positive example: Calendar.JANUARY, Calendar.FEBRUARY, Calendar.MARCH, etc. to refer to the corresponding month for parameter transfer or comparison.

(9) Notes and Statutes

5. [Mandatory] All enumeration type fields must have comments, explaining the purpose of each data item.

(11) Others

6. [Mandatory] The attribute fields of the enumeration enum (inside brackets) must be private and immutable.

6. Engineering structure

(2) Second-party library dependencies

5. [Mandatory] Second-party libraries can define enumeration types, parameters can use enumeration types, but interface return values ​​are not allowed to use enumeration types or
POJO objects containing enumeration types.

5. Example code

https://gitee.com/qbhj/java-cases/tree/master/case-javase/src/main/java/com/qbhj/casejavase/enums

6. References

"Java Development Manual (Huangshan Edition)"
"Effective Java Chinese Edition (Original Book 3rd Edition)"
https://docs.oracle.com/javase/specs/jls/se15/html/jls-8.html#jls- 8.9
https://docs.oracle.com/javase/8/docs/technotes/guides/language/enums.html

Guess you like

Origin blog.csdn.net/weixin_43582081/article/details/129334701