枚举学习笔记
2019独角兽企业重金招聘Python工程师标准>>>
枚举Enum
关键字enum可以将一组具名的值的有限集合创建为一种新的类型,而这些具名的值可以作为常规的程序组件使用,这是一种非常有用的功能。
一个简单例子
调用enum的values()方法,可以遍历enum实例。values()方法返回enum实例的数组,而且该数组中的元素严格保持其在enum中声明时的顺序。
创建enum,编译器会为你生成一个相关的类,这个类继承自java.lang.Enum。下面的例子演示了Enum提供的一些功能。
public class EnumClass {
public static void main(String[] args) {
for (Animal animal: Animal.values()) {
System.out.println(animal + " ordinal: " + animal.ordinal());
System.out.println(animal.name());
System.out.println(animal.getDeclaringClass());
}
Animal a = Enum.valueOf(Animal.class, "PIGGER");
System.out.println(a.name());
}
}
enum Animal {
PIGGER, MONKEY, DOG, CAT, ELEPHANT, KOLA
}
------------------output------------------
PIGGER ordinal: 0
PIGGER
class com.stupidzhe.jdklearning.enu.Animal
MONKEY ordinal: 1
MONKEY
class com.stupidzhe.jdklearning.enu.Animal
DOG ordinal: 2
DOG
class com.stupidzhe.jdklearning.enu.Animal
CAT ordinal: 3
CAT
class com.stupidzhe.jdklearning.enu.Animal
ELEPHANT ordinal: 4
ELEPHANT
class com.stupidzhe.jdklearning.enu.Animal
KOLA ordinal: 5
KOLA
class com.stupidzhe.jdklearning.enu.Animal
PIGGER
------------------------------------------
ordinal()方法返回一个int值,这是每个enum实例在声明时的次序,从0开始。可以使用==来比较enum实例,编译器会自动为你提供equals()和hashCode()方法。Enum类实现了Comparable接口,所以它具有compareTo()方法。同时,它还实现了Serializable接口。
如果在enum实例上调用getDeclaringClass()方法,我们就能得到该注解所属的enum类。Enum.valueOf()是在Enum中定义的static方法,它根据给定的名字返回相应的enum实例,如果不存在将抛出异常。
static import
import static com.stupidzhe.jdklearning.enu.Animal.*;
public class EnumClass {
public static void main(String[] args) {
System.out.println(PIGGER);
}
}
enum Animal {
PIGGER, MONKEY, DOG, CAT, ELEPHANT, KOLA
}
使用static import能够将enum实例的标识符带入当前的命名空间,所以无需再使用enum类型来修饰enum实例。这是一个好的想法吗?或者还是显式地修饰enum实例更好?这要看代码的复杂程度了。编译器可以保证你使用的是正确的类型,所以唯一需要单行的是,使用静态导入会不会导致你的代码令人难以理解。
总的来说,编码原则是:具体情况具体分析。
向enum中添加新方法
除了不能继承自一个enum之外,我们基本上可以将enum看作是一个常规的类。 也就是说,我们可以在enum中添加方法,enum甚至可以有main()方法。
注意:如果你打算定义自己的方法, 那么必须在enum实例序列的最后添加一个分号。 同时,Java要求你必须先定义enum实例。如果在定义enum实例之前定义了任何方法或属性,那么在编译时就会得到错误信息。
import static com.stupidzhe.jdklearning.enu.Animal.*;
public class EnumClass {
public static void main(String[] args) {
System.out.println(PIGGER);
Animal animal = Animal.PIGGER;
animal.print();
}
}
enum Animal {
// 实例序列的最后添加一个分号
PIGGER, MONKEY, DOG, CAT, ELEPHANT, KOLA;
public static void main(String[] args) {
}
public void print() {
System.out.println(this.toString());
}
}
## switch语句中的enum 在switch中使用enum,是enum提供的一项非常便利的功能。一般来说,在switch中只能使用整数值,而枚举实例天生就具有整数值的次序,并且可以通过ordinal()方法取得其次序(显然编译器帮我们做了类似的工作),因此我们可以在switch语句总会给你使用enum。
虽然一般情况下我们必须使用enum类型来就是enum实例,但是在case语句中却不亦如此,下面的例子使用enum构造了一个小型状态机:
public class EnumClass {
public static void main(String[] args) {
System.out.println(PIGGER);
Animal animal = Animal.PIGGER;
switch (animal) {
case PIGGER:
animal.print();
animal = MONKEY;
case MONKEY:
animal.print();
animal = DOG;
case DOG:
animal.print();
animal = CAT;
case CAT:
animal.print();
animal = ELEPHANT;
case ELEPHANT:
animal.print();
animal = KOLA;
case KOLA:
animal.print();
}
}
}
enum Animal {
PIGGER, MONKEY, DOG, CAT, ELEPHANT, KOLA;
public void print() {
System.out.println(this.toString());
}
}
-------output------
PIGGER
PIGGER
MONKEY
DOG
CAT
ELEPHANT
KOLA
-------------------
编译器并没有抱怨switch中没有default语句,但这并不是因为每一个Animal都有相对应的case语句。
## values()的神秘之处 前面已经提到过,编译器为你创建的enum类继承自Enum类。然而,如果你研究一下Enum类你会发现,它并没有values()方法。
通过反射来观察方法:
/**
* @author Mr.W
*/
public class EnumMethodTest {
public static void main(String[] args) {
Class<Animal> animalClass = Animal.class;
Method[] methods = animalClass.getMethods();
System.out.println("---------methods-------");
for (Method method : methods) {
System.out.println(method.getName());
}
System.out.println("------super class------");
Class superClass = animalClass.getSuperclass();
System.out.println(superClass.getName());
System.out.println("------implements-------");
Type[] ins = superClass.getGenericInterfaces();
for (Type impl: ins) {
System.out.println(impl.getTypeName());
}
}
}
-------output-------
---------methods-------
values
valueOf
name
equals
toString
hashCode
compareTo
compareTo
valueOf
getDeclaringClass
ordinal
wait
wait
wait
getClass
notify
notifyAll
------super class------
java.lang.Enum
------implements-------
java.lang.Comparable<E>
java.io.Serializable
-----------------------
答案是values()是由编译器添加的static方法。
注意:由于values()是由编译器插入到enum定义中的static方法,所以,如果你将enum实例向上转型成Enum,那么value()方法就不可访问了。不过在Class中有一个getEnumConstants()方法,所以即使Enum接口中没有value()方法,我们仍然可以通过Class对象取得所有enum实例。
## 实现,而非继承 我们知道,所有的enum类型都继承自Enum类。由于Java不支持多继承,所以你的enum不能再继承其他类。
然而我们在创建一个新的enum时,可以同时实现一个或多个接口,不过你必须有一个enum实例才能调用其上的方法。
EnumSet
Set是一种集合,只能向其中添加不重复的对象。当然,enum也要求其对象是唯一的,所以看来enum也具有集合的行为。 不过由于enum不能进行删除或添加元素,所以它只能算是不太有用的集合。
Java SE5引入EnumSet,是为了通过enum创建一种代替拼,以代替基于int的位标志。这种标志可以用来表示某种“开关”信息,不过,使用这种标志,我们最终操作的只是bit,而不是这些bit想要表达的概念。
EnumSet的设计充分考虑到了速度因素,因为它必须与非常高效的bit标志相竞争。就内部而言,它可能就是将一个long值作为比特向量,所以EnumSet非常快速高效。
EnumSet的优点就是,它在说明一个二进制位是否存在时,具有更好的表达能力,并且无需担心性能问题。
EnumSet中的元素必须来自一个enum。
public class ExtendsTestClass {
public static void main(String[] args) {
EnumSet<Animal> enumSet = EnumSet.noneOf(Animal.class);
enumSet.add(Animal.MONKEY);
enumSet.add(Animal.PIGGER);
}
}
枚举数不超过64的时候,EnumSet的实现类时RegularEnumSet,add()源码如下:
public boolean add(E e) {
typeCheck(e);
long oldElements = elements;
elements |= (1L << ((Enum<?>)e).ordinal());
return elements != oldElements;
}
可以看到,是一个long类型的位操作。
注意:枚举数大于64个,实现类会是JumboEnumSet。
EnumMap
EnumMap是一种很特殊的Map,它要求其中的键(key)必须来自一个enum。由于enum本身的限制,所以EnumMap内部可由数组实现,因此EnumMap的速度十分快。
我们需要通过enum的实例作为键来调用put()方法,其他操作与使用一般的Map差不多。
EnumMap<Animal, String> enumMap = new EnumMap<>(Animal.class);
enumMap.put(Animal.MONKEY, Animal.MONKEY.name());
System.out.println(enumMap.get(Animal.ELEPHANT));
------output------
null
------------------
与EnumSet一样,enum实例定义时的次序决定了其在EnumMap中的顺序。
enum的每个实例作为键总是存在的。
常量相关的方法
Java的enum有一个非常有趣的特性,即它允许程序员为enum实例编写方法,从而为每个enum实例赋予各自不同的行为。要实现常量相关的方法,你需要为enum定义一个或多个adbstract方法,然后为每个enum实例实现该抽象方法。
public enum Animal {
PIGGER {
public String getInfo() {
return "pigger";
}
// 覆盖
@Override
public void wow() {
System.out.println("hehe");
}
},
MONKEY {
public String getInfo() {
return "monkey";
}
},
DOG {
public String getInfo() {
return "dog";
}
},
CAT {
public String getInfo() {
return "cat";
}
},
ELEPHANT{
public String getInfo() {
return "elephant";
}
},
KOLA{
public String getInfo() {
return "kola";
}
};
// 添加抽象方法
public abstract String getInfo();
// 常量相关的方法
public void wow() {
System.out.println("haha");
}
public void print() {
System.out.println(this.toString());
}
// 直接调用即可
//System.out.println(Animal.PIGGER.getInfo());
}
转载于//my.oschina.net/StupidZhe/blog/1573605
还没有评论,来说两句吧...