java特性-枚举
2 Enumerated Types
2.1 创建enum
创建enum类型必须包括至少3个基本成分
A:enum关键字
B:enum类的名字
C:一组该类的值
一些其他可定义的成分
D:一些enum要实现的接口
E:变量定义
F:方法定义
G:特殊的赋值
看一下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | public enum Grade { A, B, C, D, F, INCOMPLETE } public class Student { private String name; private Grade grade; public Student(String name){ this.name = name; } public void setName(String name) { this.name = name; } public String getName() { return name; } public void assignGrade(Grade grade) { this.grade = grade; } public Grade getGrade() { return grade; } /** * @param args */ public static void main(String[] args) { Student studentA = new Student("A"); Student studentB = new Student("B"); Student studentC = new Student("C"); studentA.assignGrade(Grade.B); studentB.assignGrade(Grade.INCOMPLETE); studentC.assignGrade(Grade.A); } } |
注意:
A:你可以把所有的public static final的变量在enum类中声明
B:大部分时候,你只需要使用enum的基本功能
C:按照约定用大写字符定义你的enum
D:enum类像一般的class类被引用
以下是enum的基本概念:
A:enum是类
B:所有的enum类都继承java.lang.Enum
C:enum类中定义的值不是integer,而是一个java.lang.Enum的实例
D:enum类没有公有的构造器,这是为了防止创建新的值
E:enum类中的所有值都是public static final的(Enums are not “final” when they have value-specific methods)
F:enum的值可以用=或或者equasl()来计较
G:enum类都实现了java.lang.Comparable
H:enum类override toString()方法,返回类中value的name,如Grade.INCOMPLETE.toString()返回”INCOMPLETE”,toString()方法不是fianl,你可以override
I:enum类提供一个valueOf()方法,如Grade.valueOf(“INCOMPLETE”)返回Grade.INCOMPLETE
J:enum类提供一个final的方法ordinal(),返回整形的各enum值在enum类中的位置,从0开始
K:enum类提供values()方法,返回enum类中enum值的集合
2.2 Declaring Enums Inline
看一下代码
1 2 3 4 5 | public class Downloader { public enum DownloadStatus { INITIALIZING, IN_PROGRESS, COMPLETE }; . . } |
需要说明的是申明DownloadStatus就不需要再用static修饰了
2.3 Switching on Enums
看一下代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | public static void switchStatement(Student student) { switch (student.getGrade()) { case A: break; case B: break; case C: break; case D: break; case F: break; case INCOMPLETE: break; default: break; } } |
需要注意的是在这里case A而不是case Grade.A;这里只是为了简化写法,和static import没有关系
switch语句最好加上default处理,说不定有人会修改enum类,这样你的switch语句还是可以处理unknown conditions
2.4 Maps of Enums
看一下以前的写法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | public static final int INITIALIZING = 0; public static final int COMPILING = 1; public static final int COPYING = 2; public static final int JARRING = 3; public static final int ZIPPING = 4; public static final int DONE = 5; public static final int ERROR = 6; public static String[] antMessages = new String[] { "Initalizing Ant...", // INITIALIZING "Compiling Java classes...", // COMPILING "Copying files...", // COPYING "JARring up files...", // JARRING "ZIPping up files...", // ZIPPING "Build complete.", // DONE "Error occurred." // ERROR }; public static void main(String[] args) { . . int antStatus = antProcess.getStatus(); System.out.println("ant> " + antMessages[antStatus]); . } |
使用EnumMap的写法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | public enum AntStatus { INITIALIZING, COMPILING, COPYING, JARRING, ZIPPING, DONE, ERROR } import java.util.EnumMap; public class AntEnumMap { /** * @param args */ public static void main(String[] args) { EnumMap antMessages = new EnumMap(AntStatus.class); antMessages.put(AntStatus.INITIALIZING, "Initializing Ant..."); antMessages.put(AntStatus.COMPILING, "Compiling Java classes..."); antMessages.put(AntStatus.COPYING, "Copying files..."); antMessages.put(AntStatus.JARRING, "JARring up files..."); antMessages.put(AntStatus.ZIPPING, "ZIPping up files..."); antMessages.put(AntStatus.DONE, "Build complete."); antMessages.put(AntStatus.ERROR, "Error occurred."); for (AntStatus status : AntStatus.values()) { System.out.println("For status " + status + ", message is: " + antMessages.get(status)); } } } |
附件的好处是初始化EnumMap的时候不需要排序,保证enums对应message的正确性
感觉优化不是很明显,还是那么多代码要写;在创建EnumMap对象的时候如果可以写成new EnumMap(AntStatus.class,String[] strings)从而在内部完成map的装载是不是更方便?
2.5 Sets of Enums
看一下代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | public enum GuitarFeatures { ROSEWOOD, // back/sides MAHOGANY, // back/sides ZIRICOTE, // back/sides SPRUCE, // top CEDAR, // top AB_ROSETTE, // abalone rosette AB_TOP_BORDER, // abalone top border IL_DIAMONDS, // diamond/square inlay IL_DOTS // dots inlays } |
import java.util.EnumSet;
1 2 3 4 5 6 7 8 9 10 11 12 13 | public class GuitarEnumSet { public static void main(String[] args) { EnumSet allFeatures = EnumSet.allOf(GuitarFeatures.class); EnumSet backSides = EnumSet.of(GuitarFeatures.ROSEWOOD, GuitarFeatures.MAHOGANY, GuitarFeatures.ZIRICOTE); EnumSet bourgeoisD150 = EnumSet.of(GuitarFeatures.ROSEWOOD, GuitarFeatures.SPRUCE, GuitarFeatures.AB_ROSETTE, GuitarFeatures.IL_DIAMONDS); boolean hasAbRosette = bourgeoisD150.contains(GuitarFeatures.AB_ROSETTE); } } |
详细看一下api
2.6 Adding Methods to an Enum
看一下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | public enum GuitarFeatures { ROSEWOOD(0), // back/sides MAHOGANY(0), // back/sides ZIRICOTE(300), // back/sides SPRUCE(0), // top CEDAR(0), // top AB_ROSETTE(75), // abalone rosette AB_TOP_BORDER(400), // abalone top border IL_DIAMONDS(150), // diamond/square inlay IL_DOTS(0); // dots inlays private float upcharge; GuitarFeatures(float upcharge){ this.upcharge = upcharge; } public float getUpcharge() { return upcharge; } } |
为enum类添加了带float参数构造方法,类中的每个枚举值的声明都要将使用此构造方法;枚举值的定义以分号结束;变量upcharge的申明不能放到枚举值的申明之前;枚举类的构造方法是私有的,不能申明为public
2.7 Implementing Interfaces with Enums
首先构造一个interface类
1 2 3 | public interface Features { public float getUpcharge(); } |
修改enum类的定义为:
1 2 3 4 | public enum GuitarFeatures implements Features { . . } |
2.8 Value-Specific Class Bodies
看一下代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | enum Opcode { PUSH(1) { public void perform(StackMachine machine, int[] operands) { machine.push(operands[0]); } }, ADD(0) { public void perform(StackMachine machine, int[] operands) { machine.push(machine.pop( ) + machine.pop( )); } }, BEZ(1) { public void perform(StackMachine machine, int[] operands) { if (machine.pop( ) == 0) machine.setPC(operands[0]); } }; Opcode(int numOperands) { this.numOperands = numOperands; } int numOperands; public abstract void perform(StackMachine machine, int[] operands); } |
之前说过enum都是final的,除了在Value-Specific Class Bodies的情况下,实事上,上面的编码看,每个enum value都创建了一个匿名子类,编译后,除了enum类本身外,多了几个enum value的子类,又从反编译class文件看到一般的enum类都会被声明为final,而这里没有.
以上的写法往往在debug时很麻烦,以前在debug ejb代码的时候也是碰到,系统会帮你生成一些非代码级的代码调用,看一下简单的实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | enum Opcode { PUSH(1), ADD(0), BEZ(1); int numOperands; Opcode(int numOperands){ this.numOperands = numOperands; } public void perform(StackMachine machine, int[] operands) { switch (this) { case PUSH: machine.push(operands[0]); break; case ADD: machine.push(machine.pop() + machine.pop()); break; case BEZ: if (machine.pop() == 0) machine.setPC(operands[0]); break; default: throw new AssertionError(); } } } |