介绍
枚举是一一列举的意思,列出某个有穷集合的所有成员。java中的枚举是在jdk1.5出现的,在枚举类型没出现之前,开发人员是采用在接口中定义常量来代表枚举的。使用枚举有什么好处呢?首先见名知意,看见枚举名称,就能知道枚举所代表的意思;其次使用枚举类型可以有强制的类型约束。
枚举的基本原理
Java中的枚举都是集成子Enum这个抽象类,下面来看下这个类中有哪些方法和字段:
//所有枚举的父类
public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serializable {
//枚举常量的名称,即声明枚举时的变量名称
private final String name;
public final String name() {
return name;
}
//顺序,枚举定义的位置,从0开始
private final int ordinal;
public final int ordinal() {
return ordinal;
}
//唯一的构造器,此构造器由编译器执行
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;
}
public final int hashCode() {
return super.hashCode();
}
//不支持克隆
protected final Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
//按照声明的位置进行排序,谁声明在前,谁就排在前面
public final int compareTo(E o) {
Enum<?> other = (Enum<?>)o;
Enum<E> self = this;
if (self.getClass() != other.getClass() && // optimization
self.getDeclaringClass() != other.getDeclaringClass())
throw new ClassCastException();
return self.ordinal - other.ordinal;
}
//返回声明在哪个枚举类中
@SuppressWarnings("unchecked")
public final Class<E> getDeclaringClass() {
Class<?> clazz = getClass();
Class<?> zuper = clazz.getSuperclass();
return (zuper == Enum.class) ? (Class<E>)clazz : (Class<E>)zuper;
}
//返回指定枚举类型,指定名称的枚举常量
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);
}
protected final void finalize() { }
//不支持序列化
private void readObject(ObjectInputStream in) throws IOException,
ClassNotFoundException {
throw new InvalidObjectException("can't deserialize enum");
}
//不支持序列化
private void readObjectNoData() throws ObjectStreamException {
throw new InvalidObjectException("can't deserialize enum");
}
}
定义一个简单的枚举:
public enum Life{
ANIMAL,
PLANT
}
通过javap反编译后的代码:
public final class Life extends java.lang.Enum<Life> {
public static final Life ANIMAL;
public static final Life PLANT;
public static Life[] values();
public static Life valueOf(java.lang.String);
static {};
}
看了反编译的代码可以知道枚举类就是一个普通的java类,枚举常量就是枚举类的实例,还为我们的枚举类添加了静态代码块和两个方法。其实还有一个字段是枚举类数组,保存了所有枚举类实例,private static final Life[] $VALUES;
下面分析下编译器为我们做了哪些事:
首先分析静态代码块;
- 创建Life对象实例,使用继承自Enum类的构造器,赋值给枚举实例,例如ANIMAL和PLANT,当然编译器是可以拿到枚举的名称(name)以及枚举的声明位置(ordinal)
- 创建Life对象数组,把所有的枚举实例添加到数组中,然后赋值给$VALUES字段
再看添加的两个方法:
- values方法返回$VALUES的克隆值
- valueOf内部调用Enum的valueOf方法,根据枚举名称返回枚举实例
有兴趣的话可以使用javap -c 具体看看详细的步骤。
枚举类与普通类的相同点
在枚举类中枚举常量最先声明,最后一个枚举常量后面加上分号,之后才能声明字段和方法。
1、为枚举类添加字段,并通过构造器赋值
//为枚举类添加description字段
public enum Life {
//通过构造器赋值
ANIMAL("animal"),
PLANT("plant");
//可以为字段添加getter和setter方法
private String description;
//构造默认为private的并且只能是private的
Life(String description) {
this.description = description;
}
}
2、为枚举类添加方法或抽象方法,抽象方法要在枚举实例中实现
public enum Life {
ANIMAL {
@Override
public void say() {
System.out.println("animal");
}
},
PLANT {
@Override
public void say() {
System.out.println("plant");
}
};
abstract void say();
public void live() {
System.out.println("live on earth");
}
}
所以所有的枚举实例都是枚举类的子类
3、枚举类实现接口
//定义一个接口
public interface Speak {
void say();
}
//枚举类实现接口
public enum Life implements Speak{
ANIMAL {
@Override
public void say() {
System.out.println("animal");
}
},
PLANT {
@Override
public void say() {
System.out.println("plant");
}
};
}
当然还可以声明内部类或者接口,声明常量,就不列举了
使用枚举的一个小技巧
参见下方的List和Map集合
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public enum Life {
ANIMAL("animal"),
PLANT("plant");
private String classify;
Life(String classify) {
this.classify = classify;
}
//把所有的枚举类放到集合里
private static List<Life> lives = Stream.of(values()).collect(Collectors.toList());
//把所有的枚举按照classify进行分类转成一个map,当然可以根据其他的字段进行转换,具体情况具体分析
private static Map<String, Life> lifeMap = Stream.of(values()).collect(Collectors.toMap(Life::getClassify, life -> life));
public static List<Life> getLives() {
return lives;
}
public String getClassify() {
return classify;
}
public static Map<String, Life> getLifeMap() {
return lifeMap;
}
}
枚举还有两个伴生集合EnumMap和EnumSet
总结
枚举说到底还是一个java类,这也是java中枚举类强大的地方,可以添加方法和字段。