目录
- 定义
- 1)定义
- 2)内部实现
- 3)方法与源码
- 高级特性
- 1)switch用法
- 2)自定义传值与构造函数
- 3)枚举实现抽象方法
- 4)枚举注解属性
- 5)枚举实现接口
- 6)复合使用
- 总结
定义
1)定义
枚举类是Java 5引入的,在Java 5之前,Java并没有内置的枚举类型,只能通过自定义类来实现类似枚举的功能。例如:
public class EnumClass { public static final int CONSTANT1 = xxx; public static final int CONSTANT2 = xxx; public static final int CONSTANT3 = xxx; ...
}
Java 枚举类是一种特殊类型的数据结构,一般用来存储定义一些字符串,数字等数据结构。枚举类中的每个常量都称为枚举常量。枚举类在Java中使用关键字enum定义。一般枚举类格式如下:
public enum EnumClass { CONSTANT1, CONSTANT2, CONSTANT3, ...
}
其中,EnumClass为枚举类的名称,CONSTANT1、CONSTANT2、CONSTANT3等为枚举常量的名称。枚举类的引入,提供了一种更简洁、更安全的定义枚举类型的方式。
枚举类可以直接在其他类中引用,例如:
public enum Color { RED, GREEN, BLUE
}
public class ColorUtil { private ColorUtil() { } public static void main(String[] args) { Color red = Color.RED; }
}
枚举类是类,所以当然也可以在其他类内部定义:
public class Example { public enum Color { RED, GREEN, BLUE } public static void main(String[] args) { System.out.println(Color.RED); }
}
枚举类在内部定义时,可以将其作为内部类来定义,也可以将其作为静态内部类来定义,内部定义的枚举类可以访问外部类的成员变量和方法,比如:
public class Example { private static int privateVariable = 10; public enum Color { RED, GREEN, BLUE; public void print() { System.out.println(name() + " has a value of " + Example.privateVariable); } } public static void main(String[] args) { Example.Color.RED.print(); }
}
这段代码运行main方法的结果为:
2)内部实现
我们借助IDEA中的一个反编译分析插件Jadx Class Decompiler反编译刚刚创建的Color枚举类,首先安装插件如下:
安装完成后,右键点击需要反编译的java文件,这里选择Color.java:
选择01 分析字节码,可以得到分析结果如下:
可以看到,枚举类是public和final修饰的,这表示它不能像普通的类一样被继承,也可以看到枚举类继承自java.lang.Enum类型,并且定义了public static final修饰的三个实例变量RED GREEN BLUE,这三个实例变量实际都是Color的实例对象。
此外其内部还定义了静态的values方法,它会返回一个包含所有枚举实例的Color[]结构的列表,以及valuesOf(String input)方法,它通过传入对应的枚举常量字符串,返回对应的Color实例。
3)方法与源码
我们翻阅官方文档可以得知java.lang.Enum包含了如下一些方法(查看Enum类源码也能看到):
其中特有方法应属valuesOf()和ordinal(),valuesOf()刚刚已经说明,它会返回指定名称的枚举常量,例如:
public enum Color { RED, GREEN, BLUE;
} Color color = Color.valueOf("GREEN");
ordinal()方法主要是获取枚举量在枚举类中的顺序,比如在上面例子中RED排在第一位,ordinal()返回值为0,示例如下:
public static void main(String[] args) { Color red = Color.RED; int ordinal = red.ordinal(); // ordinal = 0
}
另外在字节码分析结果中可以看到其实Enum是有构造方法的,但是这个构造方法用户无法调用,只有编译器可以调用,我们翻阅java.lang.Enum源码可以看到:
另外Enum还针对枚举常量实现了compareTo方法,并且这个compareTo方法默认是根据枚举的ordinal来对比的,也就是根据枚举在枚举类的声明顺序来对比的。
高级特性
1)switch用法
我们知道一般在使用switch进行判断时,可以使用整数、字符串,甚至表达式作为条件,但其实switch也支持枚举,并且不需要使用枚举的引用,代码示例如下:
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors; public class ColorUtil { private ColorUtil() { } private static String getDescription(Color color) { switch(color) { case RED: return "The color of blood"; case GREEN: return "The color of grass"; case BLUE: return "The color of the sky"; default: return ""; } } public static void main(String[] args) { Color red = Color.RED; String description = getDescription(red); System.out.println(description); }
}
运行可以得到结果如下:
2)自定义传值与构造函数
这个特性我本人经常使用到,首先枚举类中可以定义好属性,然后自定义构造函数,在声明枚举类实例的时候传入对应的属性值,比如在业务中可能用到的isDelete字段,我们可以定义枚举类如下:
import lombok.Getter; public enum IsDeleteEnum { TRUE(1, "是"), FALSE(0, "否"), ; //值描述 @Getter private String desc; //枚举值 @Getter private Integer code; IsDeleteEnum(Integer code, String desc) { this.code = code; this.desc = desc; }
}
这里包含了两个成员变量code和desc,分别代表枚举的值和描述信息。
这个类使用了Lombok库中的@Getter注解,枚举值和名称可以被Getter方法直接访问。
如果上面这个还是初级版本,那么下面这个高级版本则更为典型和常用,我们定义了ColorEnum的枚举类,它包含RED、BLUE、GREEN三个枚举值。每个枚举值都有一个值和名称,并且有一个静态的Map用于根据值获取枚举常量。这个类还提供了一个公共的静态方法of(),用于根据给定的值返回对应的枚举常量,如果没有找到则返回null。
import lombok.Getter; import java.util.HashMap;
import java.util.Map; public enum ColorEnum { RED(0, "红色"), BLUE(1, "蓝色"), GREEN(2, "绿色"), ; // 枚举值 @Getter private Integer value; @Getter private String name; private static Map<Integer, ColorEnum> map = new HashMap<>(); static { for (ColorEnum r : ColorEnum.values()) { map.put(r.getValue(), r); } } ColorEnum(Integer value, String name) { this.value = value; this.name = name; } public static ColorEnum of(Integer value) { return map.getOrDefault(value, null); }
}
如果我想知道某个常量值x是否包含在枚举类ColorEnum 中,就可以使用of()方法:
public static void main(String[] args) { System.out.println(ColorEnum.of(0) == null); // false System.out.println(ColorEnum.of(3) == null); // true
}
运行结果如下:
3)枚举实现抽象方法
枚举中不只可以定义一些整数或是字符串常量,甚至可以重新实现抽象方法,我们定义了一个枚举类型Weekday,表示一周中的每一天。每个枚举常量都重写了抽象方法doSomething(),并实现了具体的业务逻辑,代码如下:
public enum Weekday { MONDAY { @Override public void doSomething() { System.out.println("Doing something on Monday"); } }, TUESDAY { @Override public void doSomething() { System.out.println("Doing something on Tuesday"); } }, WEDNESDAY { @Override public void doSomething() { System.out.println("Doing something on Wednesday"); } }, THURSDAY { @Override public void doSomething() { System.out.println("Doing something on Thursday"); } }, FRIDAY { @Override public void doSomething() { System.out.println("Doing something on Friday"); } }, SATURDAY { @Override public void doSomething() { System.out.println("Doing something on Saturday"); } }, SUNDAY { @Override public void doSomething() { System.out.println("Doing something on Sunday"); } }; // 抽象方法 public abstract void doSomething(); public static void main(String[] args) { Weekday day = Weekday.MONDAY; day.doSomething(); }
}
在main函数中,通过Weekday的枚举常量来调用对应的方法,这样可以根据枚举常量来执行不同的操作,大大增加代码的可读性和可维护性。
这段代码执行结果为:
4)枚举注解属性
我们自定义一个名为ColorInterface的注解用于标记字段。注解定义一个value属性,类型为我们上文创建的的ColorEnum枚举类型。注解的保留策略为RetentionPolicy.RUNTIME,表示在运行时可以访问到该注解。
import java.lang.annotation.*; @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ColorInterface { ColorEnum value();
}
如下面代码所示,我们可以通过我们自定义的ColorInterface注解标记Flower类中的属性。main方法创建了一个Flower对象,使用反射获取Flower类中的Lily字段的注解,然后通过反射获取注解的value属性,也就是我们的枚举:
import java.lang.reflect.Field; public class Flower { @ColorInterface(ColorEnum.BLUE) private int Lily; @ColorInterface(ColorEnum.RED) private int rose; public static void main(String[] args) throws Exception { Flower flower = new Flower(); Field field = Flower.class.getDeclaredField("Lily"); ColorInterface annotation = field.getAnnotation(ColorInterface.class); ColorEnum value = annotation.value(); System.out.println(value); }
}
代码运行如下:
5)枚举实现接口
Java枚举类还可以实现接口,可以为枚举类型添加更多的方法和行为,从而扩展枚举类的功能,枚举类可以具有更多的灵活性和可扩展性此外。
public interface Animal {String makeSound();String getName();
}
public enum Dog implements Animal {BROWN("Buddy", "Buddy the Brown Dog"),BLACK("Max", "Max the Black Dog");private String name;private String sound;Dog(String name, String sound) {this.name = name;this.sound = sound;}@Overridepublic String makeSound() {return sound;}@Overridepublic String getName() {return name;}public static void main(String[] args) {Animal dog = Dog.BLACK;System.out.println(dog.getName()); // 输出: MaxSystem.out.println(dog.makeSound()); // 输出: Max the Black Dog}}
运行结果如下!:
6)复合使用
前面的这些特性当然可以混在一起使用:
我们定义一个接口 Reflex ,里面实现了两个方法,一个是可以返回颜色名称,一个是可以以List形式返回它的RGB值
import java.util.List;public interface Reflex {String getColor();List<Integer> getRGB();
}
然后我们重新写一下我们的 ColorEnum 枚举类,里面的枚举常量对象既包含属性值,又重写了方法,功能比之前更强大了一些,代码如下:
import lombok.Getter;import java.util.HashMap;
import java.util.List;
import java.util.Map;public enum ColorEnum implements Reflex {RED(0, "红色") {@Overridepublic List<Integer> getRGB() {return List.of(255, 0, 0);}},BLUE(1, "蓝色") {@Overridepublic List<Integer> getRGB() {return List.of(0, 0, 255);}},GREEN(2, "绿色") {@Overridepublic List<Integer> getRGB() {return List.of(0, 255, 0);}},;// 重写Reflex接口方法@Overridepublic String getColor() {return name;}@Getter private Integer value; @Getterprivate String name;private static Map<Integer, ColorEnum> map = new HashMap<>();static { for (ColorEnum r : ColorEnum.values()) { map.put(r.getValue(), r); } } ColorEnum(Integer value, String name) { this.value = value; this.name = name; } public static ColorEnum of(Integer value) { return map.getOrDefault(value, null); }public static void main(String[] args) {ColorEnum colorEnum1 = ColorEnum.of(2);System.out.println(colorEnum1.getName());System.out.println(colorEnum1.getColor());System.out.println(colorEnum1.getRGB());}
}
main()运行结果为:
可以看到,我们既返回了枚举的属性值,又返回了重写方法的返回值,枚举是否有其它高级用法,也欢迎探索交流讨论
总结
枚举类是Java中的原始类型之一,可以作为方法参数和返回值。枚举类可以通过类内的枚举常量表示特定的值。本文从源码和字节码实现的角度对枚举类的原理做了阐述,并且还展示了枚举类的多种高级用法,枚举类不仅可以像普通的类常量一样进行访问和使用,并且可以进行多态操作,不同的枚举常量可以有不同的方法实现,并且可以作为方法的参数和返回值。
因此枚举类有如下优点:
1.提高代码的可读性。枚举类的命名规范清晰明了,能够更直观地表达代码的含义,提高代码的可读性。
2.更容易的调试和维护。枚举类中的方法和变量都在一个地方,方便调试和维护。
3.更好的类型安全性。使用枚举类可以提供更好的类型安全性,避免了使用整数或其他类型的错误。
4.可以方便的进行多态操作:枚举类支持方法的重写和多态,可以方便的扩展功能。