Java枚举类(6.9)

参考《java疯狂讲义》

1. 枚举类入门
java 5 新增enum关键字,用以定义枚举类。
它与普通类的区别如下:

  • 枚举类可以实现一个或多个接口,使用enum定义的枚举类默认继承了java.lang.Enum类,而不是默认继承Object类,因此枚举类不能显式继承继承其他父类。其中java.lang.Enum类实现了java.lang.Serializable和java.lang.Comparable接口。
  • 使用enum定义,非抽象的枚举类默认使用final修饰,因此枚举类不能派生子类。
  • 枚举类的构造器只能使用private修饰,如果省略构造器的访问控制符,则默认使用private修饰。
  • 枚举类的所有实例必须在枚举类的第一行显式列出,否则这个类永远不能产生实例。列出这些实例时,系统默认会加上public static final修饰符,无需程序员显式添加。
  • 枚举类默认提供了一个values()方法,该方法可以很方便的遍历所有枚举值。
public enum SeasonEnum
{
   // 在第一行列出4个枚举值   
   SPRING,SUMMER,FALL,WINTER;
}

如果需要使用枚举类的某个实例,可以使用EnumClass.variable的形式。

public class EnumTest
{
   public void judge(SeasonEnum s)
   {
      // switch语句里的表达式可以时枚举值
      switch (s)
      {
         case SPRING:
            System.out.println("春暖花开,正好踏青");
            break;
         case SUMMER:
            System.out.println("夏日炎炎,适合游泳");
            break;
         case FALL:
            System.out.println("秋高气爽,进补及时");
            break;
         case WINTER:
            System.out.println("冬日雪飘,围炉赏雪");
            break;
      }
   }
   public static void main(String[] args)
   {
      // 枚举类默认有一个values方法,返回该枚举类的所有实例
      for (SeasonEnum s : SeasonEnum.values())
      {
         System.out.println(s);
      }
      // 使用枚举实例时,可通过EnumClass.variable的形式访问
      new EnumTest().judge(SeasonEnum.SPRING);
   }
}

switch的控制表达式可以是任何枚举类型。不仅如此,当switch控制表达式使用枚举类型时,后面case表达式中的值直接使用枚举值的名字,无需添加枚举类作为限定。

所有的枚举类都继承了java.lang.Enum类,所以所有枚举类都可以直接使用java.lang.Enum类的方法。java.lang.Enum类提供了如下几个常用方法:

  • int compareTo(E o):该方法用于指定枚举对象的比较顺序,同一个枚举实例只能与相同类型的枚举实例进行比较。若该枚举对象在指定枚举对象之后,返回正整数,之后返回负整数,否则返回0
  • String name():返回此枚举类型的名称,用的不多,更多采用下面的方法,因为其返回更加用户友好的名称。
  • String toString():返回枚举常量的名称
  • int ordinal():返回枚举值在枚举类中的索引值(就是在声明中的位置,索引值从0开始)
  • public static <T extends Enum>T valueOf(ClassenumType,String name):用于返回指定枚举类中指定名称的枚举值。名称必须与在该枚举类中声明枚举值时所用的标识符完全匹配,不允许使用额外的空白字符。

2. 枚举类的成员变量,方法和构造器

public enum Gender
{
   MALE,FEMALE;
   // 定义一个public修饰的实例变量
   public String name;
}

public class GenderTest
{
   public static void main(String[] args)
   {
      //通过Enum的valueOf()方法来获取指定枚举类型的枚举值
      Gender g = Enum.valueOf(Gender.class , "FEMALE");
      //直接为枚举类的name实例变量赋值
      g.name = "女";
      //直接访问枚举值的name实例变量
      System.out.println(g + "代表:" + g.name);
   }
}

为了保护枚举类的成员变量,枚举类通常应该设计成不可变类,也就是说,它的成员变量值因该不允许被改变,因此建议将枚举类的成员变量用private final修饰。如果将所有成员变量都用final修饰符,那么就必须在构造器里为这些成员变量赋初值(在定义时,或者初始化块中指定初始值也可以,不过比较少见)因此应该为枚举类显示定义带参的构造器。

public enum Gender
{
   // 此处的枚举值必须调用对应的构造器来创建
   MALE("男"),FEMALE("女");
   private final String name;
   // 枚举类的构造器只能使用private修饰
   private Gender(String name)
   {
      this.name = name;
   }
   public String getName()
   {
      return this.name;
   }
}

一旦为枚举类定义了带参数的构造器,列出枚举值时就必须对应的传入参数。也就是说,在枚举类中列出枚举值时,实际上就是调用构造器创建枚举类对象,只是这里无需使用new关键字和显式调用构造器,。前面列出枚举值时无需传入参数,甚至无需括号,仅仅是因为前面的枚举类包含无参的构造器。

3. 实现接口的枚举类
枚举类也可以实现一个或多个接口。与普通类实现一个或多个接口一样,枚举类实现一个或多个接口时,也需要实现该接口所包含的抽象方法。

public enum Gender implements GenderDesc
{
   // 此处的枚举值必须调用对应的构造器来创建
   MALE("男")//②
   // 花括号部分实际上是一个类体部分
   {
      public void info()
      {
         System.out.println("这个枚举值代表男性");
      }
   },
   FEMALE("女")//②
   {
      public void info()
      {
         System.out.println("这个枚举值代表女性");
      }
   };
  private Gender(String name)
   {
      this.name = name;
   }
   public String getName()
   {
      return this.name;
   }
   public void info()//①
   {
      System.out.println(
         "这是一个用于定义性别的枚举类");
   }
}

如果由枚举类实现接口里的方法,①则每个枚举值在调用该方法时都有相同的行为方式(因为方法体一样)。如果需要每个枚举值在调用该方法时呈现出不同的行为方式,则可以让每个枚举值分别来实现该方法,每个枚举值提供不同的方法体②

当创建两个枚举值时,后面紧跟花括号,里面包含了一个方法的定义。有人可能会有点疑问。其实花括号与前面的匿名内部类语法相似。在这种情况下,当创建一个枚举值时,并不是直接创建一个枚举实例,而是创建Gender的匿名子类的实例。
疑问:枚举类不是用final修饰吗?怎么还能派生子类。
并不是所有的枚举类都使用了final修饰,非抽象的枚举类才默认使用final修饰,对于一个抽象枚举类而言——只包含了抽象方法,他就是抽象枚举类,系统会默认使用abstract修饰,而不是final修饰。

4. 包含抽象方法的枚举类

public enum Operation
{
   PLUS
   {
      public double eval(double x , double y)
      {
         return x + y;
      }
   },
   MINUS
   {
      public double eval(double x , double y)
      {
         return x - y;
      }
   },
   TIMES
   {
      public double eval(double x , double y)
      {
         return x * y;
      }
   },
   DIVIDE
   {
      public double eval(double x , double y)
      {
         return x / y;
      }
   };
   // 为枚举类定义一个抽象方法
   // 这个抽象方法由不同的枚举值提供不同的实现
   public abstract double eval(double x, double y);
   public static void main(String[] args)
   {
      System.out.println(Operation.PLUS.eval(3, 4));
      System.out.println(Operation.MINUS.eval(5, 4));
      System.out.println(Operation.TIMES.eval(5, 4));
      System.out.println(Operation.DIVIDE.eval(5, 4));
   }
}

枚举类里定义抽象方法时不能用abstract修饰符将枚举类定义成抽象类(因为系统自动会为它添加abstract),但因为枚举值需要显示创建枚举值,而不是作为父类,所以定义每个枚举值时必须为抽象方法提供实现,否则将出现编译错误。

猜你喜欢

转载自blog.csdn.net/qq_43215734/article/details/85464927
今日推荐