ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Java 枚举类型

2021-07-14 22:04:25  阅读:210  来源: 互联网

标签:Java enum 枚举 vs ROCK 类型 SCISSORS Outcome public


枚举类型

  关键字enum可以将一组具名的值有限集合创建为一种新的类型,而这些具名的值可以作为常规的程序组件使用。

1、基本enum特性

  ①values()方法返回enum实例的数组,可以遍历enum实例

  ②ordinal()方法返回一个int值,这是每个enum实例在声明时的次序,从0开始

  ③getDeclaringClass()方法能够知道其所属的enum类

  ④name()方法返回enum实例声明时的名字

  ⑤valueOf()根据给定的名字返回相应的enum实例

  ⑥Enum类实现了Comparable接口,具有compareTo()方法

  ⑦Enum类实现了Serializable接口

  ⑧通过static import可以静态导入其他包中的enum实例,无需再用enum类型来修饰enum实例

2、向enum中添加新方法

  在添加新方法时,enum实例序列的最后添加一个分号,并且必须先定义实例。

 1 public enum OzWitch {
 2     WEST("This is west"),
 3     NORTH("This is north"),
 4     EAST("This is east"),
 5     SOUTH("This is south");
 6 ​
 7     private String description;
 8     private OzWitch(String description){ this.description = description; }
 9     public String getDescription(){ return description; }
10 ​
11     @Override
12     public String toString() {
13         String id = name();
14         return id + ":" + getDescription();
15     }
16 ​
17     public static void main(String[] args) {
18         for(OzWitch witch: OzWitch.values())
19             System.out.println(witch);
20     }
21 }
22 ​
23 /*Output:
24 WEST:This is west
25 NORTH:This is north
26 EAST:This is east
27 SOUTH:This is south
28 */

 

3、values()方法

Explore[compareTo, equals, getClass, getDeclaringClass, hashCode, name, notify, notifyAll, ordinal, toString, valueOf, values, wait]

Enum[compareTo, equals, getClass, getDeclaringClass, hashCode, name, notify, notifyAll, ordinal, toString, valueOf, wait]

  对比可知在Enum类中并没有values()方法。通过反编译,可以知道values()方法是由编译器添加的static方法。同时也添加了valueOf()方法,该方法只有一个参数,而在Enum类中的valueOf()方法需要两个参数。

  将enum向上转型为Enum就无法访问values()方法,但是在Class中有一个getEnumConstants()方法,通过该方法仍然可以取得所有enum实例。

4、实现接口

  所有的enum都继承自java.lang.Enum类。由于Java不支持多重继承,所以enum不能再继承其他,但是可以实现一个或多个接口。

5、随机选取

  <T extends Enum<T>>表示T是enum实例。

 1 public class Enums {
 2     private static Random rand = new Random(47L);
 3 ​
 4     public Enums() {
 5     }
 6 ​
 7     public static <T extends Enum<T>> T random(Class<T> ec) {
 8         return (Enum)random((Object[])((Enum[])ec.getEnumConstants()));
 9     }
10 ​
11     public static <T> T random(T[] values) {
12         return values[rand.nextInt(values.length)];
13     }
14 }

 

6、使用接口组织枚举

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

  当需要与一大堆类型打交道时,接口就不如enum好用了。为了创建一个“枚举的枚举”,可以创建一个新的enum,然后用其实例包装原接口中的每一个enum类。

 1 enum Meal {
 2     APPETIZER(Food.Appetizer.class),
 3     COFFEE(Food.Coffee.class);
 4     private Food[] values;
 5     private Meal(Class<? extends Food> kind) {
 6         values = kind.getEnumConstants();
 7     }
 8     public interface Food {
 9          enum Appetizer implements Food {
10             SALAD, SOUP, SPRING_ROLLS;
11         }
12          enum Coffee implements Food {
13             BLACK_COFFEE, DECAF_COFFEE, ESPRESSO,
14             LATTE, CAPPUCCINO, TEA, HERB_TEA;
15         }
16      }
17     public Food randomSelection() {
18         return Enums.random(values);
19     }
20 }

 

7、EnumSet

  EnumSet用于替代传统的基于int的“位标志”,这种标志可以用来表示某种“开/关”信息。对同一个参数重复地调用add()方法会被忽略掉。

 1 enum AlarmPoints{START1,START2,LOBBY,OFFICE1,OFFICE2,OFFICE3,BATHROOM}
 2 ​
 3 public class EnumSets {
 4     public static void main(String[] args) {
 5         EnumSet<AlarmPoints> points = EnumSet.noneOf(AlarmPoints.class);//Empty set
 6         points.add(AlarmPoints.BATHROOM);
 7         System.out.println(points);
 8         points.addAll(EnumSet.of(AlarmPoints.OFFICE1,AlarmPoints.OFFICE2,AlarmPoints.OFFICE3));
 9         System.out.println(points);
10         points = EnumSet.allOf(AlarmPoints.class);
11         points.removeAll(EnumSet.of(AlarmPoints.START1,AlarmPoints.START2));
12         System.out.println(points);
13         points.removeAll(EnumSet.range(AlarmPoints.OFFICE1,AlarmPoints.OFFICE3));
14         System.out.println(points);
15         points = EnumSet.complementOf(points);//取差集
16         System.out.println(points);
17     }
18 }
19 ​
20 /*Output:
21 [BATHROOM]
22 [OFFICE1, OFFICE2, OFFICE3, BATHROOM]
23 [LOBBY, OFFICE1, OFFICE2, OFFICE3, BATHROOM]
24 [LOBBY, BATHROOM]
25 [START1, START2, OFFICE1, OFFICE2, OFFICE3]
26 */

 

8、EnumMap

  EnumMap是一种特殊的Map,它要求其中的键(key)必须来自一个enum。由于enum本身的限制,所以enumMap在内部可由数组实现。

  与EnumSet一样,enum实例定义时的次序决定了其在EnumMap中的顺序。enum的每个实例作为一个键,总是存在的。如果没有用put()方法来存入相应值的话,其对应的值就是null。

 1 enum AlarmPoints{START1,START2,LOBBY,OFFICE1,OFFICE2,OFFICE3,BATHROOM}
 2 ​
 3 public class EnumMaps {
 4     public static void main(String[] args) {
 5         EnumMap<AlarmPoints,Command> em =
 6                 new EnumMap<AlarmPoints, Command>(AlarmPoints.class);
 7         em.put(AlarmPoints.OFFICE1, new Command() {
 8             @Override
 9             public void action() {
10                 System.out.println("This is office1");
11             }
12         });
13         em.put(AlarmPoints.OFFICE2, new Command() {
14             @Override
15             public void action() {
16                 System.out.println("This is office2");
17             }
18         });
19         for(Map.Entry<AlarmPoints,Command> e: em.entrySet()){
20             System.out.print(e.getKey() + ":");
21             e.getValue().action();
22         }
23     }
24 }
25 ​
26 interface Command{ void action(); }
27 ​
28 /*Output:
29 OFFICE1:This is office1
30 OFFICE2:This is office2
31 */

 

9、常量相关的方法

(1)abstract方法

  要实现常量相关的方法,需要为enum定义一个或多个abstract方法,然后为每个enum实例实现该抽象方法。还可以覆盖常量相关的方法。

 1 enum ConstantSpecificMethod{
 2     DATE_TIME{
 3         String getInfo() { return "Time";  }
 4         String f(){ return "Overridden method"; }
 5     },
 6     CLASSPATH{
 7         String getInfo() { return "classpath"; }
 8     };
 9 ​
10     abstract String getInfo();
11     String f(){ return "default behavior"; }
12 ​
13     public static void main(String[] args) {
14         for(ConstantSpecificMethod csm: values())
15             System.out.println(csm + ":" + csm.getInfo() + "," + csm.f());
16     }
17 }
18 ​
19 /*Output:
20 DATE_TIME:Time,Overridden method
21 CLASSPATH:classpath,default behavior
22 */

 

(2)使用enum的职责链

  在职责链设计模式中,程序员以多种不同的方式来解决一个问题,然后将它们链接在一起。当一个请求到来时,它遍历这个链,直到链中的某个解决方案能够处理该请求。

src/com/my/chapter19/practice8.java · sumAll/Java编程思想 - 码云 - 开源中国 (gitee.com)

(3)使用enum的状态机

  枚举类型非常适合用来创建状态机。一个状态机可以具有有限个特定的状态,它通常根据输入,从一个状态转移到下一个状态,不过也可能存在瞬时状态,而一旦任务执行结束,状态机就会立刻离开瞬时状态。

10、多路分发

  如果要执行的操作包含了不止一个类型未知的对象时,需要通过多路分发来解决。要利用多路分发,就必须为每一个类型提供一个实际的方法调用。

(1)使用enum分发

 1 enum Outcome{ WIN,LOSE,DRAW }
 2 ​
 3 interface Competitor<T extends Competitor<T>>{
 4     Outcome compete(T competitor);
 5 }
 6 ​
 7 class RoShamBo{
 8     public static <T extends Competitor<T>>
 9     void match(T a,T b){
10         System.out.println(a + " vs " + b + " :" + a.compete(b));
11     }
12 ​
13     public static <T extends Enum<T> & Competitor<T>>
14     void play(Class<T> rsbClass,int size){
15         for(int i=0;i<size;i++){
16             match(Enums.random(rsbClass),Enums.random(rsbClass));
17         }
18     }
19 }
20 ​
21 public enum RoShamBo2 implements Competitor<RoShamBo2>{
22     PAPER(Outcome.DRAW,Outcome.LOSE,Outcome.WIN),
23     SCISSORS(Outcome.WIN,Outcome.DRAW,Outcome.LOSE),
24     ROCK(Outcome.LOSE,Outcome.WIN,Outcome.DRAW);
25 ​
26     private Outcome vPAPER,vSCISSORS,vROCK;
27     RoShamBo2(Outcome paper,Outcome scissors,Outcome rock){
28         this.vPAPER = paper;
29         this.vSCISSORS = scissors;
30         this.vROCK = rock;
31     }
32 ​
33     @Override
34     public Outcome compete(RoShamBo2 it) {
35         switch (it){
36             default:
37             case PAPER: return vPAPER;
38             case SCISSORS: return vSCISSORS;
39             case ROCK: return vROCK;
40         }
41     }
42 ​
43     public static void main(String[] args) {
44         RoShamBo.play(RoShamBo2.class,6);
45     }
46 }
47 ​
48 /*Output:
49 ROCK vs ROCK :DRAW
50 SCISSORS vs ROCK :LOSE
51 SCISSORS vs ROCK :LOSE
52 SCISSORS vs ROCK :LOSE
53 PAPER vs SCISSORS :LOSE
54 PAPER vs PAPER :DRAW
55 */

 

(2)使用常量相关的方法

  常量相关的方法允许我们为每个enum实例提供方法的不同实现,这可以解决多路分发。

 1 public enum RoShamBo3 implements Competitor<RoShamBo3>{
 2     ROCK{
 3         @Override
 4         public Outcome compete(RoShamBo3 opponent) {
 5             return compete(SCISSORS,opponent);
 6         }
 7     },
 8     SCISSORS{
 9         @Override
10         public Outcome compete(RoShamBo3 opponent) {
11             return compete(PAPER,opponent);
12         }
13     },
14     PAPER{
15         @Override
16         public Outcome compete(RoShamBo3 opponent) {
17             return compete(ROCK,opponent);
18         }
19     };
20 ​
21     Outcome compete(RoShamBo3 loser,RoShamBo3 oppoent){
22         return ((oppoent == this) ? Outcome.DRAW :
23                 ((oppoent == loser) ? Outcome.WIN : Outcome.LOSE));
24     }
25 ​
26     public static void main(String[] args) {
27         RoShamBo.play(RoShamBo3.class,6);
28     }
29 }
30 ​
31 /*Output:
32 PAPER vs PAPER :DRAW
33 SCISSORS vs PAPER :WIN
34 SCISSORS vs PAPER :WIN
35 SCISSORS vs PAPER :WIN
36 ROCK vs SCISSORS :WIN
37 ROCK vs ROCK :DRAW
38 */

 

(3)使用EnumMap分发

  使用EnumMap能够实现“真正的”两路分发。

 1 public enum RoShamBo4 implements Competitor<RoShamBo4>{
 2     PAPER,SCISSORS,ROCK;
 3     static EnumMap<RoShamBo4,EnumMap<RoShamBo4,Outcome>>
 4                 table = new EnumMap<RoShamBo4, EnumMap<RoShamBo4, Outcome>>(RoShamBo4.class);
 5     static {
 6         for(RoShamBo4 it : RoShamBo4.values()){
 7             table.put(it,new EnumMap<RoShamBo4, Outcome>(RoShamBo4.class));
 8         }
 9         initRow(PAPER,Outcome.DRAW,Outcome.LOSE,Outcome.WIN);
10         initRow(SCISSORS,Outcome.WIN,Outcome.DRAW,Outcome.LOSE);
11         initRow(ROCK,Outcome.LOSE,Outcome.WIN,Outcome.DRAW);
12     }
13     static void initRow(RoShamBo4 it,Outcome vPAPER,Outcome vSCISSORS,Outcome vROCK){
14         EnumMap<RoShamBo4,Outcome> row = RoShamBo4.table.get(it);
15         row.put(RoShamBo4.PAPER,vPAPER);
16         row.put(RoShamBo4.SCISSORS,vSCISSORS);
17         row.put(RoShamBo4.ROCK,vROCK);
18     }
19     public Outcome compete(RoShamBo4 it){
20         return table.get(this).get(it);
21     }
22 ​
23     public static void main(String[] args) {
24         RoShamBo.play(RoShamBo4.class,6);
25     }
26 }
27 ​
28 /*Output:
29 ROCK vs ROCK :DRAW
30 SCISSORS vs ROCK :LOSE
31 SCISSORS vs ROCK :LOSE
32 SCISSORS vs ROCK :LOSE
33 PAPER vs SCISSORS :LOSE
34 PAPER vs PAPER :DRAW
35 */

 

(4)使用二维数组

  每个enum实例都有一个固定的值(基于其声明的次序),并且可以通过ordinal()方法取得该值。因此我们可以使用二维数组,将竞争者映射到竞争结果。

 1 public enum RoShamBo5 implements Competitor<RoShamBo5>{
 2     PAPER,SCISSORS,ROCK;
 3     private static Outcome[][] table = {
 4             {Outcome.DRAW,Outcome.LOSE,Outcome.WIN},//PAPER
 5             {Outcome.WIN,Outcome.DRAW,Outcome.LOSE},//SCISSORS
 6             {Outcome.LOSE,Outcome.WIN,Outcome.DRAW},//ROCK
 7     };
 8     public Outcome compete(RoShamBo5 other){
 9         return table[this.ordinal()][other.ordinal()];
10     }
11 ​
12     public static void main(String[] args) {
13         RoShamBo.play(RoShamBo5.class,6);
14     }
15 }
16 ​
17 /*Output:
18 ROCK vs ROCK :DRAW
19 SCISSORS vs ROCK :LOSE
20 SCISSORS vs ROCK :LOSE
21 SCISSORS vs ROCK :LOSE
22 PAPER vs SCISSORS :LOSE
23 PAPER vs PAPER :DRAW
24 */

 

 

参考于《Java编程思想》,第590~619页

 

标签:Java,enum,枚举,vs,ROCK,类型,SCISSORS,Outcome,public
来源: https://www.cnblogs.com/yqsumAll/p/15013153.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有