枚举详解——张孝祥老师高新技术

来源:互联网 发布:旧版淘宝下载2015 编辑:程序博客网 时间:2024/06/05 11:15
枚举详解——张孝祥老师高新技术


为什么要有枚举
问题:要定义星期几或性别的变量,该怎么定义?假设用1-7分别表示星期一到星期日,但有人可能会写成int Weekday = 0;或即使使用常量方式也无法阻止意外。


枚举就是要让某个类型的变量的取值只能为若干个固定值中的一个,否则,编译器就会报错。枚举可以让编译器在编译时就可以控制源程序中填写的非法值,普通变量的方式在开发阶段无法实现这一目标。


用普通类如何实现枚举功能,定义一个Weekday的类来模拟枚举功能。
私有的构造方法
每个元素分别用一个公有的静态成员变量表示
可以有若干公有方法或抽象方法。采用抽象方法定义nextDay就将大量的if.else语句转移成了一个独立的类


枚举的基本应用
举例:定义一个Weekday的枚举
扩展:枚举类的values,valueOf,name,toString,ordinal等方法(记住,讲课时要先于自定义方法前介绍,讲课更流畅)
总结:枚举是一种特殊的类,其中的每个元素都是该类的一个实例对象,例如可以调用WeekDay.SUN.getClass().getName和WeekDay.class.getName().


枚举的高级应用


枚举就相当于一个类,其中也可以定义构造方法,成员变量,普通方法和抽象方法。
枚举元素必须位于枚举体中的最开始部分,枚举元素列表的后面要有分号与其他成员分隔。把枚举中的成员方法或变量等放在枚举元素的前面,编译器报告错误。


带构造方法的枚举
构造方法必须定义成私有的
如果有多个构造方法,该如何选择哪个构造方法?
枚举元素MON和MON()的效果一样,都是调用默认的构造方法。


带方法的枚举
定义枚举TrafficLamp
实现普通的next方法
实现抽象的next方法:每个元素分别是由枚举类的子类来生成的实例对象,这些子类采用类似内部类的方式进行定义。
增加上表示时间的构造方法。


枚举只有一个成员时,就可以作为一种单列的实现方式。


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


  Java代码


  public enum Action


  {


  TURN_LEFT,


  TURN_RIGHT,


  SHOOT


  }


  在Action.java中编写此段代码并编译,虽然语法上不像是在定义类,但枚举类型本质上就是一个类。所以编译完成后,会产生一个Action.class文件。


  下面的代码是说明如何使用定义好的枚举类型一个示例:


  Java代码


  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:


  ^


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


  Java代码


  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()返回值来描述枚举值。如下代码所示:


  Java代码


  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;


  }


  }


  }




可以用下面的代码测试所定义的方法是否可用。


  Java代码


  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)的构造函数,这是为了避免直接对枚举类型实例化。如下代码:


  Java代码


  enum DetailActioin2


  {


  TURN_LEFT("向左转"),TURN_RIGHT("向右转"),SHOOT("射击");


  private String description;


  //不公开的构造函数


  private DetailActioin2(String description)


  {


  this.description = description;


  }


  public String getDescription()


  {


  return description;


  }


  }


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


  Java代码


  public class Singleton


  {


  //构造函数私有,只限内部调用


  private SingleTon(){};


  private static Singleton instance = null;


  public static synchronized SingleTon getInstance()


  {


  if(instance == null)


  instance = new Singleton();


  return instance;


  }


  }


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


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


  一种实现方式如下:


  Java代码


  public interface IDescription


  {


  public String getDescription();


  }


  public enum MoreAction implements IDescription


  {


  TURN_LEFT


  {


  //实现接口上的方法


  public String getString() {return "向左转"}


  }, //注意这里的枚举值分隔使用,


  TURN_RIGHT


  {


  //实现接口上的方法


  public String getString() {return "向右转"}


  }, //注意这里的枚举值分隔使用,


  SHOOT


  {


  //实现接口上的方法


  public String getString() {return "射击"}


  }; //注意这里的枚举值结束使用;


  }


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


  Java代码


  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,如下:


  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();


  }


  执行结果与上面相同。

原创粉丝点击