Java中枚举的实现与应用详解
作者:Brain_L
前言
Java的枚举和C/C++中的枚举作用上类似,实现上不一样。
本文主要探讨下Java中枚举的实现与应用。
如果要定义常量,可能会这样定义:private static final int SUCCESS = 0;一旦这样的定义多了之后,就很难管理,容易出错,甚至重复定义。用枚举的话就很简洁清晰。
枚举原理分析
public enum EnumTest { SUCCESS, FAILURE, EXCEPTION }
enum是和class、interface同等级的关键字,那么它有什么特殊的地方呢?遇事不决看源码。。。反编译看下上面的代码
public final class EnumTest extends Enum { public static EnumTest[] values() { return (EnumTest[])$VALUES.clone(); } public static EnumTest valueOf(String s) { return (EnumTest)Enum.valueOf(com/brain/demo/enumtest/EnumTest, s); } private EnumTest(String s, int i) { super(s, i); } public static final EnumTest SUCCESS; public static final EnumTest FAILURE; public static final EnumTest EXCEPTION; private static final EnumTest $VALUES[]; static { SUCCESS = new EnumTest("SUCCESS", 0); FAILURE = new EnumTest("FAILURE", 1); EXCEPTION = new EnumTest("EXCEPTION", 2); $VALUES = (new EnumTest[] { SUCCESS, FAILURE, EXCEPTION }); } }
枚举里只有三行,结果反编译出来这么多东西。
enum其实就是实现了Enum的类,所以还是先看下Enum这个类。
public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serializable { private final String name;//枚举名称 private final int ordinal;//序数,从0开始算 protected Enum(String name, int ordinal) { this.name = name; this.ordinal = ordinal; } public String toString() { return name; } public final boolean equals(Object other) { return this==other; } protected final Object clone() throws CloneNotSupportedException { throw new CloneNotSupportedException(); } }
两个主要属性,name和ordinal。结合EnumTest反编译的结果,初始化时会调用父类(Enum)的构造方法,将name和ordinal传进去,name是枚举的名称,ordinal是声明的顺序(从0开始)。
EnumTest中还有一个VALUES数组,里面存储着所有的枚举实例,调用values方法时返回VALUES数组的clone。
调用valueOf方法时,会调用Enum的valueOf方法
public static <T extends Enum<T>> T valueOf(Class<T> enumType, String name) { T result = enumType.enumConstantDirectory().get(name); if (result != null) return result; if (name == null) throw new NullPointerException("Name is null"); throw new IllegalArgumentException( "No enum constant " + enumType.getCanonicalName() + "." + name); } //Class.java Map<String, T> enumConstantDirectory() { if (enumConstantDirectory == null) { T[] universe = getEnumConstantsShared(); if (universe == null) throw new IllegalArgumentException( getName() + " is not an enum type"); Map<String, T> m = new HashMap<>(2 * universe.length); for (T constant : universe) m.put(((Enum<?>)constant).name(), constant); enumConstantDirectory = m; } return enumConstantDirectory; } T[] getEnumConstantsShared() { if (enumConstants == null) { if (!isEnum()) return null; try { final Method values = getMethod("values"); java.security.AccessController.doPrivileged( new java.security.PrivilegedAction<Void>() { public Void run() { values.setAccessible(true); return null; } }); @SuppressWarnings("unchecked") T[] temporaryConstants = (T[])values.invoke(null); enumConstants = temporaryConstants; } // These can happen when users concoct enum-like classes // that don't comply with the enum spec. catch (InvocationTargetException | NoSuchMethodException | IllegalAccessException ex) { return null; } } return enumConstants; }
追着源码一层层调用,最后还是调用values方法,拿到包含所有枚举实例的数组。将枚举的name作为key,实例作为value放入map中。valueOf根据传入的name从map中取出对应的实例。
枚举可以用来实现单例,并且是实现单例的最佳方式。关于为什么枚举是单例的最佳实现,参见另一篇博文——几种单例的对比。
枚举应用
除了枚举自身具备的两个属性name和ordinal外,还可以自定义需要的属性,自定义方法。
public enum EnumTest { SUCCESS(0, "调用成功"), FAILURE(1, "调用失败"), EXCEPTION(-1, "调用出错"); private int status; private String msg; EnumTest(int status, String msg) { this.status = status; this.msg = msg; } public int getStatus() { return status; } public void setStatus(int status) { this.status = status; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public String getDesc() { return status + ":" + msg; } public static void main(String[] args) { System.out.println(EnumTest.FAILURE.getDesc());//1:调用失败 } }
由于enum实际上是继承了Enum类,又由于java的单继承特性,不能再继承其他类,但是仍可以通过别的方式实现类似的功能。
public enum EnumTest { SUCCESS(0, "调用成功") { @Override String getDesc() { return "恭喜调用成功"; } }, FAILURE(1, "调用失败") { @Override String getDesc() { return "调用失败,请重试"; } }, EXCEPTION(-1, "调用出错") { @Override String getDesc() { return "调用出错"; } }; private int status; private String msg; EnumTest(int status, String msg) { this.status = status; this.msg = msg; } abstract String getDesc(); public int getStatus() { return status; } public void setStatus(int status) { this.status = status; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } }
或者干脆实现接口也可以达到同样的效果。
到此这篇关于Java中枚举的实现与应用详解的文章就介绍到这了,更多相关Java枚举内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!