枚举

简介

枚举类型是Java 5中新增特性的一部分,它是一种特殊的数据类型,之所以特殊是因为它既是一种类(class)类型却又比类类型多了些特殊的约束,但是这些约束的存在也造就了枚举类型的简洁性、安全性以及便捷性

枚举类使用

枚举的Class对象

需求:需要一次性获取到所有的枚举值对象:

DayEnum[] values = DayEnum.values();
DayEnum[] enumConstants = DayEnum.class.getEnumConstants();
// 判断某个class是否为枚举类型
System.out.println(DayEnum.MONDAY.getClass().isEnum());

enum中定义抽象方法

与常规抽象类一样,enum类也可以为其定义抽象方法,然后使每个枚举实例都实现该方法,以便产生不同的行为方式,注意abstract关键字对于枚举类来说是必须的。如下:

public enum EnumDemo {
  FIRST {
    @Override
    public String getInfo() {
      return "FIRST TIME";
    }
  },
  SECOND {
    @Override
    public String getInfo() {
      return "SECOND TIME";
    }
  };

  public abstract String getInfo();
}

也可以向上抽取为一个接口

常用的7种方式

常量

在JDK1.5 之前,定义常量都是: public static final… 。现在好了,有了枚举,可以把相关的常量分组到一个枚举类型里,而且枚举提供了比常量更多的方法

public enum Color {  
  // 若后续没有代码了,此;可以省略。否则不行
  RED, GREEN, BLANK, YELLOW;  
} 

switch

JDK1.6之前的switch语句只支持int,char,enum类型,使用枚举,能让我们的代码可读性更强

enum Signal {  
  GREEN, YELLOW, RED  
}  
public class TrafficLight {  
  Signal color = Signal.RED;  
  public void change() {  
    switch (color) {  
      case RED:  
        color = Signal.GREEN;  
        break;  
      case YELLOW:  
        color = Signal.RED;  
        break;  
      case GREEN:  
        color = Signal.YELLOW;  
        break;  
    }  
  }  
}  

向枚举中添加新方法

如果打算自定义自己的方法,那么必须在enum实例序列的最后添加一个分号。而且 Java 要求必须先定义 enum 实例

public enum Color {  
  RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4);  
  // 成员变量 
  @Getter
  private String name;  
  @Getter
  private int index;  

  // 构造方法  
  private Color(String name, int index) {  
    this.name = name;  
    this.index = index;  
  }  

  // 普通静态方法方法  可通过枚举类直接调用
  public static String getName(int index) {  
    for (Color c : Color.values()) {  
      if (c.getIndex() == index) {  
        return c.name;  
      }  
    }  
    return null;  
  }
}

覆盖枚举的方法

下面给出一个toString()方法覆盖的例子

public enum Color {  
  RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4);  
  // 成员变量  
  private String name;  
  private int index;  
  // 构造方法  
  private Color(String name, int index) {  
    this.name = name;  
    this.index = index;  
  }  
  //覆盖方法  
  @Override  
  public String toString() {  
    return this.index+"_"+this.name;  
  }  
}  

实现接口(规范、统一控制非常有效)

所有的枚举都继承自java.lang.Enum类。由于Java 不支持多继承,所以枚举对象不能再继承其他类

public interface Behaviour {  
  void print();  
  String getInfo();  
}  

public enum Color implements Behaviour{  
  RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4);  
  // 成员变量  
  private String name;  
  private int index;  
  // 构造方法  
  private Color(String name, int index) {  
    this.name = name;  
    this.index = index;  
  }  

  //接口方法  
  @Override  
  public String getInfo() {  
    return this.name;  
  }  
  //接口方法  
  @Override  
  public void print() {  
    System.out.println(this.index+":"+this.name);  
  }  
}  

使用接口组织枚举

这个思想是很好的。如果一个模块需要有多个枚举,建议可以放在接口内来统一组织。这样方便管理,也方便做一些多态的使用

public interface Food {  
  enum Coffee implements Food{  
    BLACK_COFFEE,DECAF_COFFEE,LATTE,CAPPUCCINO  
  }  
  enum Dessert implements Food{  
    FRUIT, CAKE, GELATO  
  }  
} 

枚举集合的使用(EnumSet和EnumMap)

java.util.EnumSet和java.util.EnumMap是两个枚举集合。EnumSet保证集合中的元素不重复;EnumMap中的 key是enum类型,而value则可以是任意类型

EnumMap基本用法
public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V>
    implements java.io.Serializable, Cloneable

先思考一个问题,有一堆颜色不同的数据,需要统计出每种颜色的数量,定义如下枚举用于表示颜色:

enum Color {
  GREEN,RED,BLUE,YELLOW
}

显然这个如果用Map来做,估计谁都会做。但是本文采用更方便,更加高效的EnumMap来处理:

EnumMap<Color, Integer> enumMap = new EnumMap<>(Color.class);
for (Color color : list) {
  int count = enumMap.getOrDefault(color, 0) + 1;
  enumMap.put(color, count);
}

EnumMap作为枚举的专属集合,没有理由再去使用HashMap,毕竟EnumMap要求其Key必须为Enum类型,因而使用Color枚举实例作为key是最恰当不过了,也避免了获取name的步骤

更重要的是EnumMap效率更高,因为其内部是通过数组实现的。注意EnumMap的key值不能为null,虽说是枚举专属集合,但其操作与一般的Map差不多

概括性来说EnumMap是专门为枚举类型量身定做的Map实现,虽然使用其它的Map(如HashMap)也能完成相同的功能,但是使用EnumMap会更加高效

它只能接收同一枚举类型的实例作为键值且不能为null,由于枚举类型实例的数量相对固定并且有限,所以EnumMap使用数组来存放与枚举类型对应的值,毕竟数组是一段连续的内存空间,根据程序局部性原理,效率会相当高

它有三个构造函数:

// 创建一个具有指定键类型的空枚举映射
EnumMap(Class<K> keyType) ;
// 创建一个其键类型与指定枚举映射相同的枚举映射,最初包含相同的映射关系(如果有的话)   
EnumMap(EnumMap<K,? extends V> m) ;
// 创建一个枚举映射,从指定映射对其初始化
EnumMap(Map<K,? extends V> m);
//使用第一种构造
Map<Color,Integer> enumMap=new EnumMap<>(Color.class);
//使用第二种构造
Map<Color,Integer> enumMap2=new EnumMap<>(enumMap);

//使用第三种构造
Map<Color,Integer> hashMap = new HashMap<>();
hashMap.put(Color.GREEN, 2);
hashMap.put(Color.BLUE, 3);
Map<Color, Integer> enumMap = new EnumMap<>(hashMap);
EnumSet用法
public abstract class EnumSet<E extends Enum<E>> extends AbstractSet<E>
    implements Cloneable, java.io.Serializable

EnumSet 中所有元素都必须是枚举类型

与其他Set接口的实现类HashSet/TreeSet(内部都是用对应的HashMap/TreeMap实现的)不同的是,EnumSet在内部实现是位向量,它是一种极为高效的位运算操作

由于直接存储和操作都是bit,因此EnumSet空间和时间性能都十分可观,足以媲美传统上基于 int 的“位标志”运算,重要的是我们可像操作set集合一般来操作位运算,这样使用代码更简单易懂同时又具备类型安全的优势

创建EnumSet并不能使用new关键字,因为它是个抽象类,而应该使用其提供的静态工厂方法,EnumSet的静态工厂方法比较多,如下:

// 创建一个具有指定元素类型的空EnumSet。
EnumSet<E>  noneOf(Class<E> elementType)       
//创建一个指定元素类型并包含所有枚举值的EnumSet
<E extends Enum<E>> EnumSet<E> allOf(Class<E> elementType)
// 创建一个包括枚举值中指定范围元素的EnumSet
<E extends Enum<E>> EnumSet<E> range(E from, E to)
// 初始集合包括指定集合的补集
<E extends Enum<E>> EnumSet<E> complementOf(EnumSet<E> s)
// 创建一个包括参数中所有元素的EnumSet
<E extends Enum<E>> EnumSet<E> of(E e)
<E extends Enum<E>> EnumSet<E> of(E e1, E e2)
<E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3)
<E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3, E e4)
<E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3, E e4, E e5)
<E extends Enum<E>> EnumSet<E> of(E first, E... rest)
//创建一个包含参数容器中的所有元素的EnumSet
<E extends Enum<E>> EnumSet<E> copyOf(EnumSet<E> s)
<E extends Enum<E>> EnumSet<E> copyOf(Collection<E> c)
// 空集合
EnumSet<Color> enumSet = EnumSet.noneOf(Color.class);
System.out.println("添加前:" + enumSet.toString());
enumSet.add(Color.GREEN);
enumSet.add(Color.RED);
enumSet.add(Color.BLUE);
enumSet.add(Color.YELLOW);
System.out.println("添加后:" + enumSet.toString());
// 使用allOf创建包含所有枚举类型的enumSet,其内部根据Class对象初始化了所有枚举实例
EnumSet<Color> enumSet1 = EnumSet.allOf(Color.class);
System.out.println("allOf直接填充:" + enumSet1.toString());
// 初始集合包括枚举值中指定范围的元素
EnumSet<Color> enumSet2 = EnumSet.range(Color.RED, Color.YELLOW);
System.out.println("指定初始化范围:" + enumSet2.toString());
// 指定补集,也就是从全部枚举类型中去除参数集合中的元素
EnumSet<Color> enumSet3 = EnumSet.complementOf(enumSet2);
System.out.println("指定补集:" + enumSet3.toString());
// 初始化时直接指定元素
EnumSet<Color> enumSet4 = EnumSet.of(Color.RED);
System.out.println("指定Color.RED元素:" + enumSet4.toString());
EnumSet<Color> enumSet5 = EnumSet.of(Color.RED, Color.GREEN);
System.out.println("指定Color.RED和Color.GREEN元素:" + enumSet5.toString());
// 复制enumSet5容器的数据作为初始化数据
EnumSet<Color> enumSet6 = EnumSet.copyOf(enumSet5);
System.out.println("enumSet6:" + enumSet6.toString());
List<Color> list = new ArrayList<>();
list.add(Color.GREEN);
list.add(Color.GREEN);// 重复元素
list.add(Color.RED);
list.add(Color.BLUE);
System.out.println("list:" + list.toString());

//使用copyOf(Collection<E> c)  可以达到去重的效果
EnumSet enumSet7 = EnumSet.copyOf(list);
System.out.println("enumSet7:" + enumSet7.toString());

其实EnumSet最有价值的是其内部实现原理,采用的是位向量,它体现出来的是一种高效的数据处理方式,这点很值得我们去学习它

总结:多使用枚举,枚举的好处

enum这个关键字,可以理解为跟class差不多,也是个单独的类

一般的class可以自己new对象,想几个就几个,而这个enum关键字,他就不行,他的实例对象,只能在这个enum里体现。也就是说,他对应的实例是有限的。这也就是枚举的好处了,限制了某些东西的范围,举个例子:

一年四季,只能有春夏秋冬,你要是字符串表示的话,那就海了去了,但是,使用枚举的话,在enum的里把所有选项全部列出来,那么这个季节的属性,对应的值,只能在里面挑。不能有其他的

使用枚举的一些规范推荐

  • 枚举类名建议带上Enum后缀,枚举成员名称需要全部大写。单词间使用下划线分隔
  • 强制规范:所有的枚举类型成员必须要有注释,说明每个字段的用途(一般可以使用接口进行强制规范)

枚举类型对象之间的值比较,是可以使用== ,直接来比较值,是否相等的,不是必须使用equals方法。 并且,强烈建议使用==,效率更高

Commons-lang工具

EnumUtils:辅助操作枚举的一些工具
  • getEnum(Class enumClass, String enumName) 通过类返回一个枚举,可能返回空
  • getEnum(final Class enumClass, final String enumName, final E defaultEnum)
  • getEnumIgnoreCase(final Class enumClass, final String enumName)
  • getEnumIgnoreCase(final Class enumClass, final String enumName, final E defaultEnum)
  • getEnumList(Class enumClass) 通过类返回一个枚举集合
  • getEnumMap(Class enumClass) 通过类返回一个枚举map
  • isValidEnum(Class enumClass, String enumName) 验证enumName是否在枚举中
  • isValidEnumIgnoreCase(final Class enumClass, final String enumName)
public enum ImagesTypeEnum {
  JPG, JPEG, PNG, GIF
}
ImagesTypeEnum imagesTypeEnum = 
  EnumUtils.getEnum(ImagesTypeEnum.class, "JPG");
List<ImagesTypeEnum> imagesTypeEnumList = 
  EnumUtils.getEnumList(ImagesTypeEnum.class);
Map<String, ImagesTypeEnum> enumMap = 
  EnumUtils.getEnumMap(ImagesTypeEnum.class);
boolean jpg = EnumUtils.isValidEnum(ImagesTypeEnum.class, "JPG");
System.out.println(EnumUtils.isValidEnum(ImagesTypeEnum.class, null));
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

①笶侕濄

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值