概述
- 使用场景:
- 类的对象只有有限个,确定的。如:星期、性别、季节
- 当需要定义一组常量时,强烈建议使用枚举类
- 优势:
- 以这种方式定义的常量使代码更具可读性,允许进行编译时检查,预先记录可接受值的列表,并避免由于传入无效值而引起的意外行为
枚举类的实现
JDK1.5之前需要自定义枚举类
JDK 1.5 新增的 enum 关键字用于定义枚举类
- 自定义枚举类
- 私有化类的构造器,保证不能在类的外部创建其对象
- 在类的内部创建枚举类的实例。声明为:public static final
- 枚举类如果有属性,应该声明为private final,并在构造器中初始化
public class Season { //枚举类中的元素是确定可知,我们不希望他会发生改变,故定义private final private final String DESCRIBE; //构造器 private Season(String describe){ this.DESCRIBE = describe; } //构造器 private Season(){ this.DESCRIBE = "no describe"; } //这些暴露出去全局常量就是枚举类的实际 //当我们需要某个季节时,只需要将对应的全局常量赋过去,保证不管在哪里 //都会是同一个春夏秋冬 public static final Season SPRING = new Season("spring"); public static final Season SUMMER = new Season("summer"); public static final Season AUTUMN = new Season("autumn"); public static final Season WINTER = new Season("winter"); }
- 使用enum定义枚举类
- 使用 enum 定义的枚举类默认继承了 java.lang.Enum类,因此不能再继承其他类
- 枚举类的构造器只能使用 private 权限修饰符
- 枚举类的所有实例必须在枚举类中显式列出(, 分隔 ; 结尾)。列出的实例系统会自动添加 public static final 修饰
- 必须在枚举类的第一行声明枚举类对象
public enum OrderStatus { //相当于public static final OrderStatus ORDERED = new OrderStatus(); ORDERED, READY(5), DELIVERED(0); private final int DAY_TO_DELIVER; private OrderStatus(){ DAY_TO_DELIVER = 10; } private OrderStatus(int days){ DAY_TO_DELIVER = days; } }
使用
- 主要方法:
values()
:返回枚举类型的对象数组。该方法可以很方便地遍历所有的枚举值valueOf(String str)
:可以把一个字符串转为对应的枚举类对象。要求字符串必须是枚举类对象的“名字”。如不是,会有运行时异常:IllegalArgumentExceptiontoString()
:返回当前枚举类对象常量的名称,可重写自定义更适合的输出name()
:返回当前枚举类对象的名称,优先使用toString()方法equals()
:枚举类型可以直接用==比较,Enum类中实现的equals()方法也是直接用 ==实现,注:该Enum中该方法是final修饰的,实现该方法是为了在List、Set、Map中使用hashCode()
:Enum中实现了hashCode()来和equals()保持一致,它也是final修饰的getDeclaringClass()
:返回枚举常量所属枚举类型的Class对象,用来判断两个枚举常量是否属于同一个枚举类型compareTo()
:实现类Comparable接口,可以比较两个枚举常量大小(按照声明顺序)ordinal()
:得到当前枚举常量的次序clone()
:枚举类不能被克隆。为了防止子类实现克隆方法,Enum实现了一个仅抛出CloneNotSupportedException异常(运行时异常)且被final修饰的方法
System.out.println(OrderStatus.ORDERED.name());//ORDERED System.out.println(OrderStatus.ORDERED.toString());//ORDERED //得到所有枚举常量 OrderStatus[] orderStatuses = OrderStatus.values(); //得到名为ORDERED的枚举常量 OrderStatus orderStatus = OrderStatus.valueOf("ORDERED"); //得到枚举常量所处类的类名 System.out.println(orderStatus.getDeclaringClass()); System.out.println(orderStatus.ordinal());//0
- 使用==比较枚举类型
- 运行时安全性
OrderStatus orderStatus = null; System.out.println(orderStatus.equals(OrderStatus.ORDERED));//空指针异常 System.out.println(orderStatus == OrderStatus.ORDERED);//正常运行
- 编译时安全性
if (OrderStatus.ORDERED.equals(TestColor.GREEN));// 编译正常 if (OrderStatus.ORDERED == TestColor.GREEN);// 编译失败,类型不匹配
- 运行时安全性
- 在switch语句中使用枚举类型
OrderStatus orderStatus = OrderStatus.ORDERED; switch (orderStatus){ case ORDERED: System.out.println("order"); break; case READY: System.out.println("ready"); break; case DELIVERED: System.out.println("delivered"); break; default: break; }
- 枚举类方法使用
public class Pizza { private PizzaStatus status; public enum PizzaStatus { //相当于都是一个PizzaStatus匿名子类 ORDERED (5){ @Override public boolean isOrdered() { return true; } }, READY (2){ @Override public boolean isReady() { return true; } }, DELIVERED (0){ @Override public boolean isDelivered() { return true; } }; private int timeToDelivery; //如果该方法被调用,说明那个枚举类常量没有重写该方法,即不是ORDERED public boolean isOrdered() {return false;} public boolean isReady() {return false;} public boolean isDelivered(){return false;} public int getTimeToDelivery() { return timeToDelivery; } PizzaStatus (int timeToDelivery) { this.timeToDelivery = timeToDelivery; } } public boolean isDeliverable() { return this.status.isReady(); } public void printTimeToDeliver() { System.out.println("Time to delivery is " + this.getStatus().getTimeToDelivery()); } // Methods that set and get the status variable. @Test public void givenPizaOrder_whenReady_thenDeliverable() { Pizza testPz = new Pizza(); testPz.setStatus(Pizza.PizzaStatus.READY); assertTrue(testPz.isDeliverable());//false } }
通过枚举类实现设计模式
- 单例模式
public enum 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;
}
}
- 策略模式
public enum 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);
}
EnumSet和EnumMap
public class Pizza {
private static EnumSet<PizzaStatus> undeliveredPizzaStatuses =
EnumSet.of(PizzaStatus.ORDERED, PizzaStatus.READY);
private PizzaStatus status;
public enum PizzaStatus {
...
}
public boolean isDeliverable() {
return this.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.
}
- EnumSet
- EnumSet 是一种专门为枚举类型所设计的 Set 类型
- 与HashSet相比,由于使用了内部位向量表示,因此它是特定 Enum 常量集的非常有效且紧凑的表示形式
- 它提供了类型安全的替代方法,以替代传统的基于int的“位标志”,使我们能够编写更易读和易于维护的简洁代码
- EnumSet 是抽象类,其有两个实现:RegularEnumSet 、JumboEnumSet,选择哪一个取决于实例化时枚举中常量的数量
- 在很多场景中的枚举常量集合操作(如:取子集、增加、删除、containsAll和removeAll批操作)使用EnumSet非常合适;如果需要迭代所有可能的常量则使用Enum.values()
@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); }
- EnumMap:
- EnumMap是一个专门化的映射实现,用于将枚举常量用作键。与对应的 HashMap 相比,它是一个高效紧凑的实现,并且在内部表示为一个数组
@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); }