枚举类型-enum

来源:互联网 发布:如何自学数控编程 编辑:程序博客网 时间:2024/06/06 12:34

前言:枚举时Java SE5中的新特性,枚举类型的关键字为enum。在创建enum时编译器会自动添加一些有用的特性,在API中却没有显示,所以需要注意一下。

1. 基础

枚举类型的定义格式为:
enum <枚举类型名> {<枚举表>}; 

1.1 编译器自动添加

  在创建enum的时候,编译器会自动添加一些有用的特性。例如,会创建toString()方法,以便可以方便地显示某个enum实例的名字;会创建ordinal()方法,用来表示某个enum常量的声明顺序;创建 static values()方法,用来按照enum常量的声明书序,产生由这些常量值构成的数组。

示例:

定义一个枚举类型:

public enum Spiciness {  NOT, MILD, MEDIUM, HOT, FLAMING}
测试用例:

public class EnumOrder {  public static void main(String[] args) {    for(Spiciness s : Spiciness.values())      System.out.println(s + ", ordinal " + s.ordinal());  }}
输出结果:

NOT, ordinal 0MILD, ordinal 1MEDIUM, ordinal 2HOT, ordinal 3FLAMING, ordinal 4
详细的原因可以参考:Java Enum 类 的 values()方法 api没有,实例是怎么调用的?

注意:enum实例在声明时的顺序是从0开始的。可以使用==来比较enum实例,编译器会自动为你提供equals()和hashCode()方法。Enum类实现了Comparable借口,所以具有compareTo()方法,同时也实现了Serializable接口。
  在enum实例上调用getDeclaring()方法,我们就能知道其所属的enum类;name()方法返回enum实例声明时的名字,这与使用toString()方法效果相同;valueOf()是在Enum中定义的static方法,可以根据给定的名字返回相应的enum实例,如果不存在给定名字的实例,将会抛出异常。

示例:

import static net.mindview.util.Print.*;enum Shrubbery { GROUND, CRAWLING, HANGING }public class EnumClass {  public static void main(String[] args) {    for(Shrubbery s : Shrubbery.values()) {      print(s + " ordinal: " + s.ordinal());      printnb(s.compareTo(Shrubbery.CRAWLING) + " ");      printnb(s.equals(Shrubbery.CRAWLING) + " ");      print(s == Shrubbery.CRAWLING);      print(s.getDeclaringClass());      print(s.name());      print("----------------------");    }    // Produce an enum value from a string name:    for(String s : "HANGING CRAWLING GROUND".split(" ")) {      Shrubbery shrub = Enum.valueOf(Shrubbery.class, s);      print(shrub);    }  }} /* Output:GROUND ordinal: 0-1 false falseclass ShrubberyGROUND----------------------CRAWLING ordinal: 10 true trueclass ShrubberyCRAWLING----------------------HANGING ordinal: 21 false falseclass ShrubberyHANGING----------------------HANGINGCRAWLINGGROUND*/
其中net.mindview.util.Print是一个简单的工具类,只是将输出封装了一下,完全可以更改一下,或者可以从《Java编程思想》随书代码中找到,链接为:http://download.csdn.net/detail/fanxiaobin577328725/9714670

1.2 静态导入enum

  使用static import能够将enum的标识符带入当前的命名空间,所以无需要用enum类型来修饰enum实例。编译器可以确保你使用的是正确的类型,但是需要注意的是,使用静态导入会导致代码令人难以理解。这也是静态导入的缺点,所以谨慎使用。

注意:在定义enum的同一个文件中,以及在默认包中定义enum时,无法使用静态导入。

示例:

//: enumerated/Spiciness.javapackage enumerated;public enum Spiciness {  NOT, MILD, MEDIUM, HOT, FLAMING}
测试用例:

//: enumerated/Burrito.javapackage enumerated;import static enumerated.Spiciness.*;public class Burrito {  Spiciness degree;  public Burrito(Spiciness degree) { this.degree = degree;}  public String toString() { return "Burrito is "+ degree;}  public static void main(String[] args) {    System.out.println(new Burrito(NOT));    System.out.println(new Burrito(MEDIUM));    System.out.println(new Burrito(HOT));  }} /* Output:Burrito is NOTBurrito is MEDIUMBurrito is HOT*/

1.3 向enum中添加新方法

  除了不能继承一个enum之外,我们基本可以将enum看做一个常规的类。也就是说:我们可以向enum中添加方法,甚至是main()方法。

  默认的toString()方法只能返回枚举实例的名字,但是我们有时希望每个枚举实例能够返回对自身的描述。为此,我们可以提供一个构造器,专门负责处理这个额外的信息,然后添加 一个方法,返回这个描述信息。

示例:

//: enumerated/OzWitch.java// The witches in the land of Oz.package enumerated;import static net.mindview.util.Print.*;public enum OzWitch {  // Instances must be defined first, before methods:  WEST("Miss Gulch, aka the Wicked Witch of the West"),  NORTH("Glinda, the Good Witch of the North"),  EAST("Wicked Witch of the East, wearer of the Ruby " +    "Slippers, crushed by Dorothy's house"),  SOUTH("Good by inference, but missing");  private String description;  // Constructor must be package or private access:  private OzWitch(String description) {    this.description = description;  }  public String getDescription() { return description; }  public static void main(String[] args) {    for(OzWitch witch : OzWitch.values())      print(witch + ": " + witch.getDescription());  }} /* Output:WEST: Miss Gulch, aka the Wicked Witch of the WestNORTH: Glinda, the Good Witch of the NorthEAST: Wicked Witch of the East, wearer of the Ruby Slippers, crushed by Dorothy's houseSOUTH: Good by inference, but missing*/

注意:如果打算定义自己的方法,那么必须在enum实例序列的最后添加一个分号。同时,Java要求你必须先定义enum示例。

  在上面的这个例子中,虽然构造器时private,但是对于他的可访问性,其实并没有什么变化。因为(即使不声明为private)我们只能在enum定义的内部使用其构造器创建enum实例。一旦enum的定义结束,编译器就不允许我们再使用其构造器来创建任何实例了。

1.4 switch语句中的enum

  一般情况下在switch中只能使用整数值,而枚举实例天生就具备整数值的次序,并且可以通过ordinal()方法取得其次序(文章开头提及),因此我们可以在switch语句中使用enum。

  虽然一般情况下我们必须使用enum类型来修饰一个enum实例,但是在case语句中却不必如此。

示例:

//: enumerated/TrafficLight.java// Enums in switch statements.package enumerated;import static net.mindview.util.Print.*;// Define an enum type:enum Signal { GREEN, YELLOW, RED, }public class TrafficLight {  Signal color = Signal.RED;  public void change() {    switch(color) {      // Note that you don't have to say Signal.RED      // in the case statement:      case RED:    color = Signal.GREEN;                   break;      case GREEN:  color = Signal.YELLOW;                   break;      case YELLOW: color = Signal.RED;                   break;    }  }  public String toString() {    return "The traffic light is " + color;  }  public static void main(String[] args) {    TrafficLight t = new TrafficLight();    for(int i = 0; i < 7; i++) {      print(t);      t.change();    }  }} /* Output:The traffic light is REDThe traffic light is GREENThe traffic light is YELLOWThe traffic light is REDThe traffic light is GREENThe traffic light is YELLOWThe traffic light is RED*///:~
注意:此示例中的switch中没有default但是仍然编译通过,并正常运行,如果我们去除其中某个case分支也是正常的。这就意味着,我们必须自己去确保覆盖所有的分支。但是,如果在case语句中调用return,那么编译器就会强制要求有default语句了。

2. 高级

2.1 values()方法

  编译器创建的enum类都继承自java.lang.Enum类,我们都知道Java是单继承的,查看API发现没有这个方法,其实values()方法是有编译器添加的static方法。

  由于values()方法是由编译器插入到enum定义中的static方法,所以你讲enum实例向上转型为Enum,那么values()方法就不可访问了,

示例:

//: enumerated/UpcastEnum.java// No values() method if you upcast an enumpackage enumerated;enum Search { HITHER, YON }public class UpcastEnum {  public static void main(String[] args) {    Search[] vals = Search.values();    Enum e = Search.HITHER; // Upcast//     e.values(); // No values() in Enum    for(Enum en : e.getClass().getEnumConstants())      System.out.println(en);  }} /* Output:HITHERYON*///:~
注意:因为getEnumConstants()是Class上的方法,所以我们可以对不是枚举的类调用此方法。

2.2 实现,而非继承

  我们已经知道,所有的Enum都继承自java.lang.Enum类。由于Java不支持多重继承,所以你的Enum不能再继承其他类。然而,在我们创建一个新的enum时,可以同时实现一个或多个接口:

//: enumerated/cartoons/EnumImplementation.java// An enum can implement an interfacepackage enumerated.cartoons;import java.util.*;import net.mindview.util.*;enum CartoonCharacter implements Generator<CartoonCharacter> {SLAPPY, SPANKY, PUNCHY, SILLY, BOUNCY, NUTTY, BOB;private Random rand = new Random(47);public CartoonCharacter next() {return values()[rand.nextInt(values().length)];}}public class EnumImplementation {public static <T> void printNext(Generator<T> rg) {System.out.print(rg.next() + ", ");}public static void main(String[] args) {// Choose any instance:CartoonCharacter cc = CartoonCharacter.BOB;for (int i = 0; i < 10; i++)printNext(cc);}}/* * Output: BOB, PUNCHY, BOB, SPANKY, NUTTY, PUNCHY, SLAPPY, NUTTY, NUTTY, * SLAPPY, */// :~

2.3 随机选取

  许多时候我们需要从enum实例中进行随机选择。我们可以利用泛型,从而使得这个工作更一般化,可以作为工具使用。

//: net/mindview/util/Enums.javapackage net.mindview.util;import java.util.*;public class Enums {  private static Random rand = new Random(47);  public static <T extends Enum<T>> T random(Class<T> ec) {    return random(ec.getEnumConstants());  }  public static <T> T random(T[] values) {    return values[rand.nextInt(values.length)];  }}
理解这一段代码需要一点泛型的基础,其中<T extends Enum<T>>表示T是一个enum实例。下面是编写的使用示例:

//: enumerated/RandomTest.javapackage enumerated;import net.mindview.util.*;enum Activity { SITTING, LYING, STANDING, HOPPING,  RUNNING, DODGING, JUMPING, FALLING, FLYING }public class RandomTest {  public static void main(String[] args) {    for(int i = 0; i < 20; i++)      System.out.print(Enums.random(Activity.class) + " ");    System.out.println();    System.out.println("-----------------------------------");    for(int i = 0; i < 20; i++)    System.out.print(Enums.random(Activity.values()) + " ");  }} /* Output:STANDING FLYING RUNNING STANDING RUNNING STANDING LYING DODGING SITTING RUNNING HOPPING HOPPING HOPPING RUNNING STANDING LYING FALLING RUNNING FLYING LYING -----------------------------------LYING HOPPING LYING JUMPING DODGING SITTING DODGING LYING RUNNING DODGING SITTING FALLING FALLING JUMPING LYING HOPPING LYING HOPPING RUNNING SITTING *///:~

2.4 使用接口组织枚举

  有时我们需要扩展原来enum中的元素,或者希望使用子类将一个enum中的元素进行分类,但是无法从enum继承子类,很让人头痛。

  我们可以在一个借口的内部,创建实现该接口的枚举,以此将元素进行分组,可以达到枚举元素分类组织的目的。

举例来说:假设想用enum来表示不同的类别的食物,同时还希望每个enum元素仍然保持Food类型,实现如下:

//: enumerated/menu/Food.java// Subcategorization of enums within interfaces.package enumerated.menu;public interface Food {  enum Appetizer implements Food {    SALAD, SOUP, SPRING_ROLLS;  }  enum MainCourse implements Food {    LASAGNE, BURRITO, PAD_THAI,    LENTILS, HUMMOUS, VINDALOO;  }  enum Dessert implements Food {    TIRAMISU, GELATO, BLACK_FOREST_CAKE,    FRUIT, CREME_CARAMEL;  }  enum Coffee implements Food {    BLACK_COFFEE, DECAF_COFFEE, ESPRESSO,    LATTE, CAPPUCCINO, TEA, HERB_TEA;  }}
  对于enum而言,实现接口是使其子类化的唯一办法,所以嵌入在Food中的每个enum都实现了Food接口,我们可以说“所有东西都是某种类型的Food”。因为enum类型实现了Food接口,那么我们就可以将其实例向上转型为Food
同样还有一种更简洁的管理枚举的办法,就是将一个enum嵌套在另一个enum内,如下:

//: enumerated/menu/Meal2.javapackage enumerated.menu;import net.mindview.util.*;public enum Meal2 {  APPETIZER(Food.Appetizer.class),  MAINCOURSE(Food.MainCourse.class),  DESSERT(Food.Dessert.class),  COFFEE(Food.Coffee.class);  private Food[] values;  private Meal2(Class<? extends Food> kind) {    values = kind.getEnumConstants();  }  public interface Food {    enum Appetizer implements Food {      SALAD, SOUP, SPRING_ROLLS;    }    enum MainCourse implements Food {      LASAGNE, BURRITO, PAD_THAI,      LENTILS, HUMMOUS, VINDALOO;    }    enum Dessert implements Food {      TIRAMISU, GELATO, BLACK_FOREST_CAKE,      FRUIT, CREME_CARAMEL;    }    enum Coffee implements Food {      BLACK_COFFEE, DECAF_COFFEE, ESPRESSO,      LATTE, CAPPUCCINO, TEA, HERB_TEA;    }  }  public Food randomSelection() {    return Enums.random(values);  }  public static void main(String[] args) {    for(int i = 0; i < 5; i++) {      for(Meal2 meal : Meal2.values()) {        Food food = meal.randomSelection();        System.out.println(food);      }      System.out.println("---");    }  }}

3. 拓展

3.1 使用EnumSet替代标志

  对集合有了解对EnumSet应该理解起来就不难了,Set是集合中的一种,其要求添加不重复的对象。enum也要求其成员都是唯一的,所以enum看起来也具有集合的行为,但是enum中是无法删除或添加元素,所以有点鸡肋了。Java SE 5引入EnumSet,是为了通过enum创建一种替代品,以替代传统的基于int的“位标志”,EnumSet中的元素必须来自一个enum。

示例:

//: enumerated/AlarmPoints.javapackage enumerated;public enum AlarmPoints {  STAIR1, STAIR2, LOBBY, OFFICE1, OFFICE2, OFFICE3,  OFFICE4, BATHROOM, UTILITY, KITCHEN} 
然后我们用EnumSet来进行操作:

//: enumerated/EnumSets.java// Operations on EnumSetspackage enumerated;import java.util.*;import static enumerated.AlarmPoints.*;import static net.mindview.util.Print.*;public class EnumSets {  public static void main(String[] args) {    EnumSet<AlarmPoints> points =      EnumSet.noneOf(AlarmPoints.class); // Empty set    points.add(BATHROOM);    print(points);    points.addAll(EnumSet.of(STAIR1, STAIR2, KITCHEN));    print(points);    points = EnumSet.allOf(AlarmPoints.class);    points.removeAll(EnumSet.of(STAIR1, STAIR2, KITCHEN));    print(points);    points.removeAll(EnumSet.range(OFFICE1, OFFICE4));    print(points);    points = EnumSet.complementOf(points);    print(points);  }} /* Output:[BATHROOM][STAIR1, STAIR2, BATHROOM, KITCHEN][LOBBY, OFFICE1, OFFICE2, OFFICE3, OFFICE4, BATHROOM, UTILITY][LOBBY, BATHROOM, UTILITY][STAIR1, STAIR2, OFFICE1, OFFICE2, OFFICE3, OFFICE4, KITCHEN]*/


3.2 使用EnumMap

————————》》后期完善
&#8195&#8195

参考资料:

《Java编程思想 第四版》

最后修改时间:2017年3月29日16:34:34

********************************************************************************结束语********************************************************************************************
  我在写这篇博客的时候也是一名初学者,有任何疑问或问题请留言,或发邮件也可以,邮箱为:fanxiaobin.fxb@qq.com,我会尽早的进行更正及更改。
在我写过的博客中有两篇博客是对资源的整理,可能对大家都有帮助,大家有兴趣的话可以看看!!
下载资料整理——目录:http://blog.csdn.net/fanxiaobin577328725/article/details/51894331
  这篇博客里面是我关于我见到的感觉不错的好资源的整理,里面包含了书籍及源代码以及个人搜索的一些资源,如果有兴趣的可以看看,我会一直对其进行更新和添加。
优秀的文章&优秀的学习网站之收集手册:http://blog.csdn.net/fanxiaobin577328725/article/details/52753638
  这篇博客里面是我对于我读过的,并且感觉有意义的文章的收集整理,纯粹的个人爱好,大家感觉有兴趣的可以阅读一下,我也会时常的对其进行更新。
********************************************************************************感谢********************************************************************************************

0 0