枚举Enum类还能实现接口!教你玩转枚举
枚举是什么?
枚举是一种特殊的数据类型,预先定义一组常量(对象),并且必须为其赋值。
Java 枚举类型的基本想法非常简单:
这些类通过共有的静态final域为每个枚举常量导出一个实例。枚举类型没有可以访问的构造器,所以它是真的final类。客户端不能创建枚举类型的实例,也不能对它进行扩展,因此不存实例,而只存在声明过程的枚举常量。也就是枚举类型是实例受控的。它们是单例(Singleton)的范型化,本质上是单元素的枚举。
枚举使代码更易拓展和不一定代码减少,对比见下:
我们使用1234用来表示一年中的四个季节
1、常规代码
public String getSeasonString(Integer key) { switch (key) { case 1: return "SPRING"; case 2: return "SUMMER"; case 3: return "AUTUMN"; case 4: return "WINTER"; default: return null; } }
2、使用枚举
// 定义枚举类 @JsonFormat(shape = JsonFormat.Shape.OBJECT) public enum SeasonTwoArgs { /** * 春天 */ SPRING(1, "春天"), SUMMER(2, "夏天"), AUTUMN(3, "秋天"), WINTER(4, "冬天"); Integer key; String msg; SeasonTwoArgs(int key, String season) { this.key = key; this.msg = season; // System.out.println("初始化:" + this.name() + "," + this.msg + "," + season); } // 很多情况,我们可能从前端拿到的值是枚举类的 key ,然后就可以通过以下静态方法获取到对应枚举值 public static SeasonTwoArgs valueofKey(Integer key) { for (SeasonTwoArgs season : SeasonTwoArgs.values()) { if (season.key == key) { return season; } } throw new IllegalArgumentException("No element matches " + key); } public String getMsg() { return msg; } public int getKey() { return key; } public void setKey(int key) { this.key = key; } public void setMsg(String msg) { this.msg = msg; } // 获取 季节的方法 public String getSeasonString(Integer key) { return SeasonTwoArgs.valueofKey(key).msg; }
说了,在使用的时候,我都是调工具类里面的getSeasonString(String key)方法啊!
对比看来,发现代码并没有减少,反而增加了。
但是,如果后期发生了改变,使用枚举更加的方便,也不会影响到之前的业务逻辑。
枚举的要求:
- 它们限制了列名变量可以采取的值。
- 它们迫使您考虑列名可以采取的所有可能的值。
- 它们是一个常数,而不是一个数字,增加了源代码的可读性
自定义多参数枚举
代码见上。
枚举类型保证了编译时的类型安全。包含同名常量的多个枚举类型可以在一个系统中和平共处。因为每个类型都有自己的命名空间。可以新增或者重新排列枚举类型中的常量,而无需重新编译它的客户端代码。
枚举 使用 == 的安全性
1、代码编译时期:
如果俩个枚举类型不同,编译就会报错。
if (Season.SPRING.equals(TestSeason.AUTUMN)) ; // 编译正常
if (Season.SPRING == TestSeason.AUTUMN) ; // 编译失败,类型不匹配
2、运行时期:
Season season = null; System.out.println(season == Season.SPRING);//正常运行 System.out.println(Season.SPRING.equals(season));//正常运行 System.out.println(season.equals(Season.SPRING));//空指针异常
switch中使用枚举
将枚举作为switch的参数
switch (season) { case SPRING: { System.out.println(1); return 1; } case SUMMER: { System.out.println(2); return 2; } case AUTUMN: { System.out.println(3); return 3; } case WINTER: { System.out.println(4); return 4; } default: System.out.println(0); return 0; }
如何输出JSON
1、自定义toJSONString方法
public String gtoJSONString() { JSONObject object = new JSONObject(); object.put("name", this.getName()); return object.toJSONString(); }
2、借助jackson工具
1、类上面增加注解: @JsonFormat(shape = JsonFormat.Shape.OBJECT) public enum Season { 2、get方法或者基本属性增加注解: @JsonProperty("name") public String getName() { return this.name(); }
重写方法
/** * 枚举计算类 */ public enum Operation { PLUS("+") { @Override public double apply(double x, double y) { return x + y; } }, MINUS("-") { @Override public double apply(double x, double y) { return x - y; } }, TIMES("*") { @Override public double apply(double x, double y) { return x * y; } }, DIVIDE("/") { @Override public double apply(double x, double y) { return x / y; } }; private final String symbol; Operation(String symbol) { this.symbol = symbol; } public abstract double apply(double x, double y); @Override public String toString() { return symbol; } public static void main(String[] args) { double x = 4; double y = 2; for (Operation operation : Operation.values()) { System.out.printf("%f %s %f = %f%n", x, operation, y, operation.apply(x, y)); } } }
通过接口扩展枚举
虽然枚举类型是不可扩展的,但是接口类型确实可扩展的,它是用来表示API中的操作的接口类型。你可以定义另一个枚举类型,它实现这个接口,并用这个新类型的实例代替基本类型。
定义接口
public interface IOperation { double apply(double x, double y); }
枚举实现:
/** * 枚举计算类 */ public enum Operation implements IOperation{ PLUS("+") { @Override public double apply(double x, double y) { return x + y; } }, MINUS("-") { @Override public double apply(double x, double y) { return x - y; } }, TIMES("*") { @Override public double apply(double x, double y) { return x * y; } }, DIVIDE("/") { @Override public double apply(double x, double y) { return x / y; } }; private final String symbol; Operation(String symbol) { this.symbol = symbol; } @Override public String toString() { return symbol; } public static void main(String[] args) { double x = 4; double y = 2; for (Operation operation : Operation.values()) { System.out.printf("%f %s %f = %f%n", x, operation, y, operation.apply(x, y)); } } }
扩展实现乘积运算:
public enum ExtOperation implements IOperation { EXP("^") { @Override public double apply(double x, double y) { return Math.pow(x, y); } }; private final String symbol; ExtOperation(String symbol) { this.symbol = symbol; } @Override public String toString() { return this.symbol; } //入参实现IOperation接口并且是枚举类型。这个可以将该限定去掉,只要实现IOperation接口即可。 private static <T extends Enum<T> & IOperation> void test(Class<T> tClass, double x, double y) { for (IOperation operation : tClass.getEnumConstants()) { System.out.printf("%f %s %f = %f%n", x, operation, y, operation.apply(x, y)); } } public static void main(String[] args) { test(ExtOperation.class,2,3);//使用扩展实现枚举对象 test(Operation.class,2,3);//使用默认的实现 } }
可见:
枚举类型可以添加任意的方法和域,并实现任意的接口。它们提供了所有的Object方法的高级实现,实现了Comparable和Serializable接口,并针对枚举类型的可任意改变性提供了序列化方法。
EnumSet和EnumMap
1、EnumSet
EnumSet 是一种专门为枚举类型所设计的 Set 类型。
与HashSet相比,由于使用了内部位向量表示,因此它是特定 Enum 常量集的非常有效且紧凑的表示形式。
EnumSet 是抽象类,其有两个实现:RegularEnumSet 、JumboEnumSet,选择哪一个取决于实例化时枚举中常量的数量。但是 RegularEnumSet 和JumboEnumSet无法实例化,访问访问不是public
@Test public void testEnumSet() { // 使用场景 可用于 一批数据中,计算数据处于某种状态的有多少条/是否包含等 final EnumSet<Season> enumSet = EnumSet.of(Season.SPRING, Season.WINTER); System.out.println("enumSet = " + enumSet); enumSet.add(Season.SUMMER); System.out.println("enumSet = " + enumSet); System.out.println("enumSet.contains(Season.SPRING) = " + enumSet.contains(Season.SPRING)); }
2、EnumMap
EnumMap
是一个专门化的映射实现,用于将枚举常量用作键。与对应的 HashMap
相比,它是一个高效紧凑的实现,并且在内部表示为一个数组:
@Test public void testEnumMap() { //EnumMap是一个专门化的映射实现,用于将枚举常量用作键。 final EnumMap<Season, List<Person>> enumMap = new EnumMap<>(Season.class); // 写一个投票的场景, 模拟10个人 回答自己喜欢的季节 for (int i = 0; i < 10; i++) { List<Person> personList = null; Season season = null; if (i < 3) { season = Season.SPRING; } else if (i < 4) { season = Season.SUMMER; } else if (i < 5) { season = Season.AUTUMN; } else { season = Season.WINTER; } if (enumMap.containsKey(season)) { personList = enumMap.get(season); } else { personList = new ArrayList<>(); } personList.add(new Person("name" + i, String.valueOf(i % 2), i + 18)); enumMap.put(season, personList); } enumMap.forEach((k, v) -> { System.out.println("喜欢" + k + "有" + v.size() + "个"); }); // 输出: /** * 喜欢SPRING有3个 * 喜欢SUMMER有1个 * 喜欢AUTUMN有1个 * 喜欢WINTER有5个 */ }
关于实现单例模式,天生就是单例模式!
扫码领红包微信赞赏
支付宝扫码领红包