读Java编程思想随笔の枚举类
基本enum特性
创建enum时,编译器会为你生成一个相关的类,这个类继承自java.lang.Enum。
1 enum Shrubbery{ 2 GROUND,CRAWLING,HANGING 3 } 4 public class EnumClass { 5 public static void main(String[] args) { 6 for (Shrubbery s:Shrubbery.values()){ 7 System.out.println(s+" ordinal:"+s.ordinal()); 8 System.out.println(s.compareTo(Shrubbery.CRAWLING)+" "); 9 System.out.println(s.equals(Shrubbery.CRAWLING)+" "); 10 System.out.println(s==Shrubbery.CRAWLING); 11 System.out.println(s.getDeclaringClass()); 12 System.out.println(s.name()); 13 System.out.println("--------------------------"); 14 } 15 for (String s:"HANGING CRAWLING GROUND".split(" ")){ 16 Shrubbery shrub = Enum.valueOf(Shrubbery.class,s); 17 System.out.println(shrub); 18 } 19 } 20 } 21 //GROUND ordinal:0 22 // -1 23 // false 24 // false 25 //class com.qiqi.enums.Shrubbery 26 // GROUND 27 // -------------------------- 28 // CRAWLING ordinal:1 29 // 0 30 // true 31 // true 32 //class com.qiqi.enums.Shrubbery 33 // CRAWLING 34 // -------------------------- 35 // HANGING ordinal:2 36 // 1 37 // false 38 // false 39 //class com.qiqi.enums.Shrubbery 40 // HANGING 41 // -------------------------- 42 // HANGING 43 // CRAWLING 44 // GROUND
ordinal()方法返回一个int值,这是每个enum实例在声明时的次序,从0开始。可以使用==来比较enum实例,编译器会自动为你供equals()和hashcode()方法。Enum类实现了Comparable接口,所以它具有compareTo()方法。同时,它还实现了Serializable接口。如果在enum实例上调用getDeclaringClass方法,我们就知道其所属的enum类。name()方法返回enum实例声明的名字,这与使用toString()方法效果相同。valueOf()是在Enum中定义的static方法,它根据给定的名字返回相应的enum实例,如果不存在给定名字的实例,将会抛出异常。
静态导入enum
1 import static com.qiqi.enums.Spiciness.*;//静态导入枚举类 2 /** 3 * @ClassName: Burrito 4 * @Description: 静态导入枚举类 5 * @Author huhailang 6 * @Date 2015/12/22 19:58 7 * @Company 中国奇奇科技有限公司 8 */ 9 public class Burrito { 10 Spiciness degree; 11 public Burrito(Spiciness degree){ 12 this.degree=degree; 13 } 14 15 @Override 16 public String toString() { 17 return "Burrito is "+degree; 18 } 19 20 public static void main(String[] args) { 21 System.out.println(new Burrito(NOT)); 22 System.out.println(new Burrito(MEDIUM)); 23 System.out.println(new Burrito(FLAMING)); 24 } 25 } 26 // Burrito is NOT 27 // Burrito is MEDIUM 28 //
向enum中添加新方法
1 public enum OzWitch { 2 WEST("miss west"), 3 NORTH("mis north"), 4 EAST("miss east"), 5 SOUTH("miss south"); 6 private String description; 7 private OzWitch(String description){ 8 this.description=description; 9 } 10 public String getDescription(){ 11 return description; 12 } 13 14 public static void main(String[] args) { 15 for (OzWitch witch:OzWitch.values()){ 16 System.out.println(witch+":"+witch.getDescription()); 17 } 18 } 19 } 20 // WEST:miss west 21 // NORTH:mis north 22 // EAST:miss east 23 // SOUTH:miss south
注意,如果你打算定义自己的方法,那么必须在enum实例序列的最后添加一个分号。同时,Java要求你必须先定义enum实例。如果在定义enum实例之前定义了任何方法或属性,那么在编译时就会得到错误信息。
enum中的构造器与方法和普通的类没有区别,因为除了少许限制之外,enum就是一个普通的类。
覆盖enum的方法
1 public enum SpaceShip { 2 SCOUT,CARGO,TRANSPORT; 3 4 @Override 5 public String toString() { 6 String id = name(); 7 String lower = id.substring(1).toLowerCase(); 8 return id.charAt(0)+lower; 9 } 10 public static void main(String[] args) { 11 for (SpaceShip s:values()){ 12 System.out.println(s); 13 } 14 } 15 } 16 // Scout 17 // Cargo 18 // Transport
覆盖enum方法与覆盖普通类方法没有区别。
switch语句中的enum
1 enum Signal{ 2 GREEN,YELLOW,RED 3 } 4 public class TrafficLight { 5 Signal color = Signal.RED; 6 public void change(){ 7 /**一般情况下,我们必须使用enum类型来修饰一个enum实例,但是case语句中却不必如此*/ 8 switch (color){ 9 case RED: color = Signal.GREEN; 10 break; 11 case GREEN:color = Signal.YELLOW; 12 break; 13 case YELLOW:color = Signal.RED; 14 break; 15 } 16 } 17 @Override 18 public String toString() { 19 return "The traffic light is "+color; 20 } 21 22 public static void main(String[] args) { 23 TrafficLight t = new TrafficLight(); 24 for (int i=0;i<7;i++){ 25 System.out.println(t); 26 t.change(); 27 } 28 } 29 }
编译器并没有抱怨switch中没有default语句,但这并不是因为每一个Signal都有对应的case语句。如果你注释掉其中的某个case语句,编译器同样不会抱怨什么。这意味着,你必须确保自己覆盖了所有的分支。但是如果在case语句中调用return,那么编译器就会抱怨缺少default语句了。
探讨values()
1 enum Explore{ 2 HERE,THREE 3 } 4 public class Reflection { 5 public static Set<String> analyze(Class<?> enumClass){ 6 System.out.println("Interfaces:"); 7 for (Type t:enumClass.getGenericInterfaces()){ 8 System.out.println(t); 9 } 10 System.out.println("Base:"+enumClass.getSuperclass()); 11 System.out.println("Methods:"); 12 Set<String> methods = new TreeSet<String>(); 13 for (Method m:enumClass.getMethods()){ 14 methods.add(m.getName()); 15 } 16 System.out.println(methods); 17 return methods; 18 } 19 20 public static void main(String[] args) { 21 Set<String> exploreMethods = analyze(Explore.class); 22 Set<String> enumMethods = analyze(Enum.class); 23 System.out.println(exploreMethods.containsAll(enumMethods)); 24 System.out.println("Explore.removeAll(Enum):"); 25 exploreMethods.removeAll(enumMethods); 26 System.out.println(exploreMethods); 27 //OSExecute.command("javap Explore"); 28 } 29 } 30 // Interfaces: 31 // Base:class java.lang.Enum 32 // Methods: 33 // [compareTo, equals, getClass, getDeclaringClass, hashCode, name, notify, notifyAll, ordinal, toString, valueOf, values, wait] 34 // Interfaces: 35 // java.lang.Comparable<E> 36 // interface java.io.Serializable 37 // Base:class java.lang.Object 38 // Methods: 39 // [compareTo, equals, getClass, getDeclaringClass, hashCode, name, notify, notifyAll, ordinal, toString, valueOf, wait] 40 // true 41 // Explore.removeAll(Enum): 42 // [values]
我们知道,编译器为你创建的enum类都继承自Enum类。然而,如果你研究一下Enum类就会发现,它并没有values()方法。答案是values()方法是由编译器添加的static方法。可以看出,在创建Explore的过程中,编译器还为其添加了valueOf()方法。这可能有点迷惑,Enum类不是已经有valueOf()方法了吗。不过Enum中valueOf()方法需要两个参数,而这个新增的方法只需一个参数。
1 enum Search{ 2 HITHER,YON 3 } 4 public class UpcastEnum { 5 public static void main(String[] args) { 6 Search [] vals = Search.values(); 7 Enum e = Search.HITHER; 8 /**Enum通过getEnumConstants()取得enum实例*/ 9 for (Enum en:e.getClass().getEnumConstants()){ 10 System.out.println(en); 11 } 12 } 13 } 14 // HITHER 15 // YON
enum实例实现多个接口
1 enum CartoonCharacter implements Generator<CartoonCharacter>{ 2 SLAPPY,SPANKY,PUNCHY,SILLY,BOUNCY,NUTTY,BOB; 3 private Random rand = new Random(47); 4 @Override 5 public CartoonCharacter next() { 6 return values()[rand.nextInt(values().length)]; 7 } 8 } 9 public class EnumImplementation { 10 public static <T> void printNext(Generator<T> rg){ 11 System.out.println(rg.next()+","); 12 } 13 14 public static void main(String[] args) { 15 CartoonCharacter cc = CartoonCharacter.BOB; 16 for (int i=0;i<10;i++){ 17 printNext(cc); 18 } 19 } 20 }
我们已经知道,所有enum都继承自java.lang.Enum类。由于Java不支持多重继承,所以enum不能继承其他类然而,在我们创建一个新的enum时,我们可以同时实现一个或多个接口。
随机选取
1 public class Enums { 2 private static Random rand = new Random(47); 3 public static <T extends Enum<T>> T random(Class<T> ec) { 4 return random(ec.getEnumConstants()); 5 } 6 public static <T> T random(T[] values) { 7 return values[rand.nextInt(values.length)]; 8 } 9 } ///:~
古怪的语法<T extends Enum<T>> 表示T是一个enum实例。而将Class<T>作为参数的话,我们就可以利用Class对象得到enum实例的数组了。重载后的random()方法只需使用T[]作为参数,因为它并不会调用Enum上的任何操作,它只需从数组中随机选择一个元素即可。这样,最终的返回类型正式enum类型。
Enum只是一个相当短小的类,但是你会发现,它能消除很多冗余的代码。
1 enum Activity{ 2 SITTING,LYING,STANDING,HOPPING,RUNNING,DODGING,JUMPING,FALLING,FLYING 3 } 4 public class RandomTest { 5 public static void main(String[] args) { 6 for (int i=0;i<20;i++){ 7 System.out.println(Enums.random(Activity.class)+" "); 8 } 9 } 10 } 11 // STANDING 12 // FLYING 13 // RUNNING 14 // STANDING 15 // RUNNING 16 // STANDING 17 // LYING 18 // DODGING 19 // SITTING 20 // RUNNING 21 // HOPPING 22 // HOPPING 23 // HOPPING 24 // RUNNING 25 // STANDING 26 // LYING 27 // FALLING 28 // RUNNING 29 // FLYING 30 // LYING