程序中经常会使用到一些常数,如果这些常数是共享的,在java中可以定义一个类或接口来统一管理常数。其他对象从这些类或接口取用常数。如果需要修改常数则可以从这些类或接口上直接修改,而不用更改程序的其他部分。
使用接口存储常数:
public interface Usual {
public static final int TURN_LEFT=1;
public static final int TURN_RIGHT=2;
public static final int SHOT=3;
}
在使用类存储常数时,直接在类中进行定义即可
public class Action{
public static final int TURN_LEFT=1;
..............................
}
对于简单的常数设置,以上两种方法已经够用了。在DK.1.5引用了枚举类型,带来了极大的方便
1 简单的常数设置
2 获得编译时类型检查
3 增强代码的可读性
枚举类型的性质:
- 定义枚举类型其实就是在定义一个类,很多细节是由编译器搞定,所以从某种程度上,enum关键字的作用可以看作是interface和class
- 使用enum定义枚举类型时,定义出的类型是继承自java.lang.Enum类,而每个被枚举的成员其实就是定义的枚举类型的一个实例,他们都默认为final,无法改变常数名设置的值,它们也是public和static的成员,可以通过类名称直接使用它们。
- 枚举类型的构造函数不得是public和protected的构造函数,这是为了避免粗心的开发人员直接对枚举类型进行实例化
- 因值而异的类实现方式
- Enum覆盖了toString方法,默认返回字符串本身
- Enum提供了valueOf方法,这个方法和toString方法是相对应的,调用valueOf("name")将会返回enum.name,因此我们在重写toString方法的时候要注意到这一点,一般来说应该相对应的重写valueOf方法
枚举类型的方法
- values()方法:取得所有的枚举成员实例,并以数组方式返回
- ordinal()方法:依枚举顺序得到位置索引,默认从0开始
实例演示:
1 获得编译类型检查
定义一个接口
public interface Usual {
public static final int TURN_LEFT=1;
public static final int TURN_RIGHT=2;
public static final int SHOT=3;
}
public static void main(String[] args) throws IOException{
doAction(Action.TURN_LEFT);
}
public static void doAction(int action){
switch(action){
case Usual.TURN_LEFT:
System.out.println("向左走");
break;
case Usual.TURN_RIGHT:
System.out.println("向右走");
break;
case Usual.SHOT:
System.out.println("射击");
break;
}
return;
}
这种做法本身是没错的,但是doAction方法接受的是int类型的常数,我们是没有能力阻止开发人员对其输入规定外的常数,也没有检查switch中枚举的值是不是正确的,因为参数action这是int类型而已,当然可以自行设计一些检查动作,这需要一些额外的工作。而枚举可以轻易的解决这些问题
定义一个枚举类
public enum Action {
TURN_LEFT,
TURN_RIGHT,
SHOT;
public String get(){
switch(this.ordinal()){
case 0:
return "向右转";
case 1:
return "向左转";
case 2:
return "射击";
default:
return null;
}
}
}
public static void main(String[] args) throws IOException{
doAction(Action.TURN_LEFT);
}
public static void doAction(Action action){
switch(action){
case TURN_LEFT:
System.out.println("向左走");
break;
case TURN_RIGHT:
System.out.println("向右走");
break;
case SHOT:
System.out.println("射击");
break;
}
return;
}
这里doAction的参数类型是Action,如果对方法输入其他类型的变量,编译器就会报告错误。
使用枚举类型还可以做进一步的检查,如果在switch中加入了不属于Action中枚举的值,编译器也会报告错误
public static void doAction(Action action){
switch(action){
case TURN_LEFT:
System.out.println("向左走");
break;
case TURN_RIGHT:
System.out.println("向右走");
break;
case SHOT:
System.out.println("射击");
break;
}
case STOP:
System.out.println("停止");
break;
return;
}
这是编译器会显示错误:unqualified enumeration constant name required
case STOP:
^
2 枚举实例的简单应用
下面这段代码我是从http://www.cnblogs.com/linjiqin/archive/2011/02/11/1951632.html借鉴而来并做了很大的修改【自己想偷懒些】
/**
* 枚举用法详解
*/
public class TestEnum {
/**
* 普通枚举
*/
public enum ColorEnum {
red, green, yellow, blue;
public static int length(){
return ColorEnum.values().length;
}//定义取得枚举成员的个数即长度
}
public enum SeasonEnum {
//注:枚举写在最前面,否则编译出错
spring, summer, autumn, winter;
private final static String position = "test";
public static SeasonEnum getSeason() {
if ("test".equals(position))
return spring;
else
return winter;
}
}
/**
* 性别
*
* 实现带有构造器的枚举
*/
public enum Gender{
//通过括号赋值,而且必须带有一个参构造器和一个属性跟方法,否则编译出错
//赋值必须都赋值或都不赋值,不能一部分赋值一部分不赋值;如果不赋值则不能写构造器,赋值编译也出错
MAN("MAN"), WOMEN("WOMEN");
private final String value;
//构造器默认也只能是private, 从而保证构造函数只能在内部使用
Gender(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
/**
*
* 实现带有抽象方法的枚举
*/
public enum OrderState {
/** 已取消 */
CANCEL {public String getName(){return "已取消";}},
/** 待审核 */
WAITCONFIRM {public String getName(){return "待审核";}},
/** 等待付款 */
WAITPAYMENT {public String getName(){return "等待付款";}},
/** 正在配货 */
ADMEASUREPRODUCT {public String getName(){return "正在配货";}},
/** 等待发货 */
WAITDELIVER {public String getName(){return "等待发货";}},
/** 已发货 */
DELIVERED {public String getName(){return "已发货";}},
/** 已收货 */
RECEIVED {public String getName(){return "已收货";}};
public abstract String getName();//这里是很重要的
}
public static void compareToSeasonEnum(SeasonEnum inputseasonenum){
System.out.println("请输入"+inputseasonenum);
for(SeasonEnum el:SeasonEnum.values()){
System.out.println(el.compareTo(inputseasonenum));
}
}//比较SeasonEnum枚举对象间的顺序
public static void main(String[] args) {
System.out.println("===============================");
ColorEnum colorEnum = ColorEnum.blue;
switch (colorEnum) {
case red:
System.out.println("color is red");
break;
case green:
System.out.println("color is green");
break;
case yellow:
System.out.println("color is yellow");
break;
case blue:
System.out.println("color is blue");
break;
}
System.out.println("===============================\n\n");
System.out.println("=====遍历ColorEnum枚举中的值====");
for(ColorEnum color : ColorEnum.values()){
System.out.println(color);
}
System.out.println("===============================\n\n");
System.out.println("====ColorEnum枚举中的值有"+ColorEnum.length()+"个====");
System.out.println(ColorEnum.red.ordinal());//0
System.out.println(ColorEnum.green.ordinal());//1
System.out.println(ColorEnum.yellow.ordinal());//2
System.out.println(ColorEnum.blue.ordinal());//3
System.out.println(ColorEnum.red.compareTo(ColorEnum.green));//-1
System.out.println("===============================\n\n");
System.out.println("===============================");
System.out.println("季节为" + SeasonEnum.getSeason());
System.out.println("===============================\n\n");
System.out.println("===============================");
for(Gender gender : Gender.values()){
System.out.println(gender.value);
}
System.out.println("===============================\n\n");
System.out.println("===============================");
for(OrderState order : OrderState.values()){
System.out.println(order.getName());
}
System.out.println("===============================\n\n");
System.out.println("========valueOf方法应用========");
//静态的valueOf方法可以让指定的字符串尝试转换为枚举实例
compareToSeasonEnum(SeasonEnum.valueOf("winter"));
System.out.println("===============================\n\n");
System.out.println("******EnumSet的用法********");
// EnumSet的使用
EnumSet<ColorEnum> stateSet = EnumSet.allOf(ColorEnum.class);
for (ColorEnum s : stateSet) {
System.out.println(s);
}
System.out.println("******EnumSet的用法********");
// EnumSet的使用
EnumSet<ColorEnum> stateSet = EnumSet.allOf(ColorEnum.class);
for (ColorEnum s : stateSet) {
System.out.println(s);
}
System.out.println("******EnumMap的用法********");
//EnumMap的使用
EnumMap stateMap = new EnumMap(ColorEnum.class);
stateMap.put(ColorEnum.red, "is red");
stateMap.put(ColorEnum.green, "is green");
stateMap.put(ColorEnum.yellow, "is yellow");
stateMap.put(ColorEnum.blue, "is blue");
for (ColorEnum s : ColorEnum.values()) {
System.out.println(s.name() + ":" + stateMap.get(s));
}
}
}
运行结果:
===============================
color is blue
===============================
=====遍历ColorEnum枚举中的值====
red
green
yellow
blue
===============================
====ColorEnum枚举中的值有4个====
0
1
2
3
-1
===============================
===============================
季节为spring
===============================
===============================
MAN
WOMEN
===============================
===============================
已取消
待审核
等待付款
正在配货
等待发货
已发货
已收货
===============================
========valueOf方法应用========
请输入winter
-3
-2
-1
0
===============================
******EnumSet的用法********
red
green
yellow
blue
******EnumMap的用法********
red:is red
green:is green
yellow:is yellow
blue:is blue
总结:
抛开具体编程语言来看,枚举所具有的核心功能应该是:
◆类型安全(Type Safety)
◆紧凑有效的枚举数值定义(Compact, Efficient Declaration of Enumerated Values)
◆无缝的和程序其它部分的交互操作(Seamless integration with other language features)
◆运行的高效率(Runtime efficiency)
1.类型安全
枚举的申明创建了一个新的类型。它不同于其他的已有类型,包括原始类型(整数,浮点数等等)和当前作用域(Scope)内的其它的枚举类型。当你对函数的参数进行赋值操作的时候,整数类型和枚举类型是不能互换的(除非是你进行显式的类型转换),编译器将强制这一点。
2. 紧凑有效的枚举数值定义
定义枚举的程序应该很简单。
3.无缝的和程序其它部分的交互操作
语言的运算符,如赋值,相等/大于/小于判断都应该支持枚举。枚举还应该支持数组下标以及witch/case语句中用来控制流程的操作。
4. 运行的高效率
枚举的运行效率应该和原始类型的整数一样高。在运行时不应该由于使用了枚举而导致性能比使用整数有所下降。如果一种语言满足这四点要求,那么我们可以说这种语言是真正的支持枚举。比如前面所说的Pascal,Ada,和C++。很明显,Java不是。
Java的创始人James Gosling是个资深的C++程序员,他很清楚什么是枚举。但似乎他有意的删除了Java的枚举能力。其原因我们不得而知。可能是他想强调和鼓励使用多态性(polymorphism),不鼓励使用多重分支。而多重分支往往是和枚举联合使用的。不管他的初衷如何,我们在Java中仍然需要枚举。