Java枚举类型的用法

1. 在J2SE5.0中要定义枚举类型是使用enum关键词,枚举类型主要提供一些常数。如下列代码定义了Action枚举类型:

public enum Action
{
     TURN_LEFT,
     TURN_RIGHT,
     SHOOT
}

在Action.java中编写此段代码并编译,虽然语法上不像是在定义类,但枚举类型本质上就是一个类。所以编译完成后,会产生一个Action.class文件。下面的代码是说明如何使用定义好的枚举类型一个示例:

public class EnumDemo
{
	public static void main(String[] args) 
	{
		doAction(Action.TURN_RIGHT);
	}

	public static void doAction(Action action)
	{
		switch(action)
		{
			case TURN_LEFT:
				System.out.println("向左转");
			    break;
			case TURN_RIGHT:
			    System.out.println("向右转");
				break;
			case SHOOT:
				System.out.println("射击");
			    break;
		}
	}
}

 运行结果:向右转。其中doAction()方法所接受的变量必须是Action枚举类型,如果对此方法输入其他类型的变量,编译器会报告错误。另外,如果在上面的switch中加入了不属于Action中枚举的值,编译器也会报告错误。例如:在上面代码case SHOOT段下面再加上以下代码:

case STOP:
        System.out.println("STOP");
        break;

则在编译时编译器会显示以下错误:

   unqualified enumeration constant name required

   case STOP:

   ^

可以在一个独立的文件中声明枚举值,或是在某个类中声明枚举成员。例如:

public class EnumDemo2
{
    private enum InnerAction {TURN_LEFT,TURN_RIGHT,SHOOT};

	public static void main(String[] args) 
	{
		doAction(InnerAction.TURN_RIGHT);
	}

	public static void doAction(InnerAction action)
	{
		switch(action)
		{
			case TURN_LEFT:
				System.out.println("向左转");
			    break;
			case TURN_RIGHT:
			    System.out.println("向右转");
				break;
			case SHOOT:
				System.out.println("射击");
			    break;
		}
	}
}

由于枚举类型本质上还是类,所以这段代码中枚举声明方式有些像在声明内嵌类。在编译完EnumDemo2.java后,会有一些额外的 .class文件产生,在此例中就是EnumDemo2$InnerAction.class与EnumDemo2$1.class。看到这两个文件,就可以知道实际上编译器产生了成员内部类和匿名内部类。

  上面通过枚举类型设定常数的方式比旧版本的常数设定方式多了编译时期类型检查的好处。以下将深入讨论枚举类型的一些知识,以便深入理解。

2. 深入枚举类型:

   定义枚举类型其实就是在定义一个类,只不过很多细节由编译器帮你补齐了,所以,某种程度上enum关键词的作用就像是class或interface.

   当使用enum定义枚举类型时,实际上所定义出来的类型是继承自java.lang.Enum类。而每个被枚举的成员其实就是定义的枚举类型的一个实例,它们都被默认为final。无法改变常数名称所设定的值,它们也是public和static的成员,这与接口中的常量限制相同。可以通过类名称直接使用它们。

   如1中所定义的枚举类型Action,TURN_LEFT,TURN_RIGHT,SHOOT都是Action的一个对象实例。因为是对象,所以,对象上自然有一些方法可以调用。如从Object继承焉的toString()方法被重新定义了,可以让你直接取得枚举值的字符串描述;values()方法可以让您取得所有的枚举成员实例,并以数组方式返回。您可以使用这两个方法来简单的将Action的枚举成员显示出来。valueOf()方法可以让您将指定的字符串尝试转换为枚举类型。可以用compareTo()方法来比较两个枚举对象在枚举时的顺序。-1之前,0位置相同,1之后对于每个枚举成员,使用ordinal()方法,依枚举顺序得到位置索引,默认以0开始。

3.枚举上的方法:定义枚举类型基本上就是在定义类,定义枚举类型时也可以定义方法。如可以为枚举加上一些描述,而不是使用默认的toString()返回值来描述枚举值。如下代码所示:

public enum DetailAction
{
     TURN_LEFT,TURN_RIGHT,SHOOT;

     public String getDescription()
        {
              switch(this.ordinal())
                  {
                        case 0:
                            return "向左转";
                        case 1:
                            return "向右转";
                        case 2:
                            return "射击";
                          default:
                            return    null;
                  }
        }
}
可以用下面的代码测试所定义的方法是否可用:

public class DetailActionDemo
{
        public static void main(String[] args)
              {
                     for(DetailAction action : DetailAction.values())
                          {
                                   System.out.printf("%s: %s%n",action,action.getDescription());
                          }
              }
}

运行结果:

TURN_LEFT: 向左转
TURN_RIGHT: 向右转
SHOOT: 射击

4.枚举类型既然是类,那么也就可以有构造函数。只不过 不得有公开(Public)的构造函数 ,这是为了避免直接对枚举类型实例化。如下代码:

public class DetailActioin2
{
     TURN_LEFT("向左转"),TURN_RIGHT("向右转"),SHOOT("射击");
     
     private String description;
     
     //不公开的构造函数
   private DetailAction2(String description)
       {
             this.description = description;
       }

     public String getDescription()
       {
             return description;
       }
}
 

非公开的构造函数最常见的一个例子就是singleton模式的应用,当某个类只能有一个实例时,可由类维护唯一的实例,这时可以将构造函数设定为私有,取用此类的开发人员就不能自行新增多个实例了。Singleton模式的简易版本代码如下:

public class Singleton
{
     //构造函数私有,只限内部调用
   private SingleTon(){};
  
     private static Singleton instance = null;
   
     public static synchronized SingleTon getInstance()
         {
                if(instance == null)
                   instance = new Singleton();
                return instance;
         }
}


5. 因值而异的类实现(Value-Specific Class Bodies)

    这个功能简单地说像是在使用匿名内部类来实现Command模式,它可以为每个枚举值定义各自的类本体与方法实现。

    一种实现方式如下:

public interface IDescription
{
    public String getDescription();
}

public enum MoreAction implements IDescription
{
    TURN_LEFT
    {
         //实现接口上的方法
      public String getDescription() {return "向左转"}
    },  //注意这里的枚举值分隔使用,
   
    TURN_RIGHT
    {
         //实现接口上的方法
      public String getDescription() {return "向右转"}
    },  //注意这里的枚举值分隔使用,

    SHOOT
    {
         //实现接口上的方法
      public String getDescription() {return "射击"}
    }; //注意这里的枚举值结束使用;
}

每个枚举成员的“{”与“}”之间是类本体,还可以在其中如同定义类一样地声明数据成员或者数据方法。测试这段代码的程序如下:

public class MoreActionDemo
{
     public static void main(String[] args)
       {
            for(MoreAction action : MoreAction.values())
               {
                    System.out.printf("%s: %s%n",action,action.getDescription());
               }
       }
}

这个例子是将因值而异的类实现用在返回枚举值的描述上,可以按照相同的方式,为每个枚举值加上一些各自的方法实现,而调用的接口是统一的。执行结果如下:

 

D:\Java_Test>javac IDescription.java

D:\Java_Test>javac MoreAction.java

D:\Java_Test>javac MoreActionDemo.java

D:\Java_Test>java MoreActionDemo
TURN_LEFT: 向左转
TURN_RIGHT: 向右转
SHOOT: 射击

 

可能是利用枚举类型实现的接口中的方法,这里直接用

 D:\Java_Test>javac IDescription.java 编译时会提示找不到getDescription()方法,所以,只好挨个来编译了。

也可以运用抽象方法去改写上面的MoreAction.java,如下:

public enum MoreAction2
{
	TURN_LEFT{
		//实现抽象方法
		public String getDescription()
		{
			return "向左转";
		}
	},  //记得这里的枚举值分隔使用,
	
	TURN_RIGHT{
		//实现抽象方法
		public String getDescription()
		{
			return "向右转";
		}
	},

	SHOOT{
		//实现抽象方法
		public String getDescription()
		{
			return "射击";
		}
	};  //记得这里的枚举值结束使用;

	//声明抽象方法
	public abstract String getDescription();
}

然后用MoreActionDemo2.java来测试:

public class MoreActionDemo2
{
     public static void main(String[] args)
       {
            for(MoreAction2 action : MoreAction2.values())
               {
                    System.out.printf("%s: %s%n",action,action.getDescription());
               }
       }
}

6.另一个完整的测试代码及运行结果和枚举类型的几点事项:

package com.csdn.myEnum;
 
import java.util.EnumMap;
import java.util.EnumSet;
 
public class LightTest {
 
    // 1. 定义枚举类型
    public enum Light {
       // 利用构造函数传参
       RED (1), GREEN (3), YELLOW (2);
 
       // 定义私有变量
       private int nCode ;
 
       // 构造函数,枚举类型只能为私有
       private Light( int _nCode) {
           this . nCode = _nCode;
       }
 
       @Override
       public String toString() {
           return String.valueOf ( this . nCode );
       }
    }
 
    /**
      * @param args
      */
    public static void main(String[] args ) {
 
       // 1. 遍历枚举类型
       System. out .println( " 演示枚举类型的遍历 ......" );
       testTraversalEnum ();
 
       // 2. 演示 EnumMap 对象的使用
       System. out .println( " 演示 EnmuMap 对象的使用和遍历 ....." );
       testEnumMap ();
 
       // 3. 演示 EnmuSet 的使用
       System. out .println( " 演示 EnmuSet 对象的使用和遍历 ....." );
       testEnumSet ();
    }
 
    /**
      * 演示枚举类型的遍历
      */
    private static void testTraversalEnum() {
       Light[] allLight = Light.values ();
       for (Light aLight : allLight) {
           System. out .println( " 当前灯 name : " + aLight.name());
           System. out .println( " 当前灯 ordinal : " + aLight.ordinal());
           System. out .println( " 当前灯: " + aLight);
       }
    }
 
    /**
      * 演示 EnumMap 的使用, EnumMap 跟 HashMap 的使用差不多,只不过 key 要是枚举类型
      */
    private static void testEnumMap() {
       // 1. 演示定义 EnumMap 对象, EnumMap 对象的构造函数需要参数传入 , 默认是 key 的类的类型
       EnumMap<Light, String> currEnumMap = new EnumMap<Light, String>(
              Light. class );
       currEnumMap.put(Light. RED , " 红灯 " );
       currEnumMap.put(Light. GREEN , " 绿灯 " );
       currEnumMap.put(Light. YELLOW , " 黄灯 " );
 
       // 2. 遍历对象
       for (Light aLight : Light.values ()) {
           System. out .println( "[key=" + aLight.name() + ",value="
                  + currEnumMap.get(aLight) + "]" );
       }
    }
 
    /**
      * 演示 EnumSet 如何使用, EnumSet 是一个抽象类,获取一个类型的枚举类型内容 <BR/>
      * 可以使用 allOf 方法
      */
    private static void testEnumSet() {
       EnumSet<Light> currEnumSet = EnumSet.allOf (Light. class );
       for (Light aLightSetElement : currEnumSet) {
           System. out .println( " 当前 EnumSet 中数据为: " + aLightSetElement);
       }
 
    }
}

执行结果如下:

演示枚举类型的遍历 ......

当前灯 name : RED

当前灯 ordinal : 0

当前灯: 1

当前灯 name : GREEN

当前灯 ordinal : 1

当前灯: 3

当前灯 name : YELLOW

当前灯 ordinal : 2

当前灯: 2

演示 EnmuMap 对象的使用和遍历 .....

[key=RED,value= 红灯 ]

[key=GREEN,value= 绿灯 ]

[key=YELLOW,value= 黄灯 ]

演示 EnmuSet 对象的使用和遍历 .....

当前 EnumSet 中数据为: 1

当前 EnumSet 中数据为: 3

当前 EnumSet 中数据为: 2

以下内容可能有些无聊,但绝对值得一窥

1.    代码:

public class State {

public static final int ON = 1;

public static final Int OFF= 0;

}

 

有什么不好了,大家都这样用了很长时间了,没什么问题啊。

首先,它不是类型安全的。你必须确保是int

其次,你还要确保它的范围是0 和1

最后,很多时候你打印出来的时候,你只看到 1 和0 ,

 

但其没有看到代码的人并不知道你的企图,抛弃你所有旧的public static final 常量吧

 

2.    可以创建一个enum 类,把它看做一个普通的类。除了它不能继承其他类了。(java 是单继承,它已经继承了Enum),

可以添加其他方法,覆盖它本身的方法

3.    switch() 参数可以使用enum 了

 

4.    values() 方法是编译器插入到enum 定义中的static 方法,所以,当你将enum 实例向上转型为父类Enum 是,values() 就不可访问了。解决办法:在Class中有一个getEnumConstants() 方法,所以即便Enum 接口中没有values() 方法,我们仍然可以通过Class 对象取得所有的enum 实例

 

5.    无法从enum 继承子类,如果需要扩展enum 中的元素,在一个接口的内部,创建实现该接口的枚举,以此将元素进行分组。达到将枚举元素进行分组。

 

6.    使用EnumSet 代替标志。enum 要求其成员都是唯一的,但是enum 中不能删除添加元素。

 

7.    EnumMap 的key 是enum ,value 是任何其他Object 对象。

 

8.    enum 允许程序员为eunm 实例编写方法。所以可以为每个enum 实例赋予各自不同的行为。

 

9.    使用enum 的职责链(Chain of Responsibility) . 这个关系到设计模式的职责链模式。以多种不同的方法来解决一个问题。然后将他们链接在一起。当一个请求到来时,遍历这个链,直到链中的某个解决方案能够处理该请求。

 

10.   使用enum 的状态机

 

11.   使用enum 多路分发


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值