《JAVA编程思想》学习笔记:第19章(枚举)

第十九章、枚举类型

概述:

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

创建enum时,编译器会为你自动生成一个相关类,此类自动extends java.lang.Enum类。

1. 基本enum特性

Enum类提供的功能如下:

values() 返回enum实例的数组,而且保持声明的顺序:

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

== 来比较enum实例,编译器会自动提供equals和hashCode方法。

getDeclaringClass() 获取其所属的enum类。

name() 返回enum实例声明时的名字,与使用toString()效果相同。

Enum.valueOf() 是在Enum中定义的static方法,他根据给定的名字返回相应的enum实例,如果不存在会抛出异常。

1.1 将静态导入用于enum

使用static import能够将enum实例的标识符代入当前的命名空间,所以无需再用enum类型来修饰enum实例。

唯一担心的是使用静态导入会不会导致代码令人难以理解。

import static enumerated.Spiciness*;

1.2 向enum中添加新方法

除了不能继承自一个enum之外,基本上可以将enum看做一个常规类。

也就是说,可以添加方法,甚至可以有main方法。

必须先定义enum实例,如果在实例之前定义任何方法或属性,编译时会报错。

一旦enum的定义结束,编译器就不允许在使用其构造器来创建任何实例了。

public enum OzWitch {

    // Instances must be defined first, before methods:

    WEST("Miss Gulch"),

    NORTH("Glinda"),

    EAST("Wicked"),

    SOUTH("Good");//必须在enum实例序列的最后添加一个分号。

    private String description;

    // Constructor must be package or private access:

    private OzWitch(String description) {

        this.description = description;

    }

    public String getDescription() {

        return description;

    }

    @Override

    public String toString() {}

    public static void main(String[] args) {

        for (OzWitch witch : OzWitch.values()) {

            print(witch + ": " + witch.getDescription());

        }

    }

}

2. switch语句中的enum

一般来说switch中只能使用整形值;

而枚举类型天生就具备整形值的次序,并且可以通过ordinal()方法获取其次序。

enum Signal {

    GREEN, YELLOW, RED,

}

public class TrafficLight {

    Signal color = Signal.RED;

    public void change() {

        switch (color) {

            case RED:

            case GREEN:

            case YELLOW:

        }

    }

}

3.values()的神秘之处

enum类都自动继承自Enum类(编译器实现),我们可以查看Enum中并没有values()方法。利用反射机制查看究竟:

values()是由编译器自动添加的static方法。同时创建Explore的过程中,编译器还添加了valueOf()方法。不是Enum类已经有valueOf()方法了吗,不过Enum中的ValueOf()方法需要两个参数,这个新增方法只需一个参数。由于Set只存储方法的名字,不考虑签名,所以removeAll只剩下values。

由于values()方法有编译器插入到enum定义中的static方法,所以enum向上转型为Enum,那么values就不可访问了,不过Class中有一个getEnumConstants方法,所以即便Enum接口中没有vlaues方法,仍然可以通过Class对象取得所有enum实例:

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

        }

    }

}

getEnumConstants() 获取所有Enum对象的实例

5. 实现,而非继承

不能extends:Java不支持多重继承;并编译器自动extends了Enum类了;

可以implements:创建一个新的enum,可以同时实现一个或多个接口。

enum CartoonCharacter implements Generator<CartoonCharacter> {

    SLAPPY, SPANKY, PUNCHY, SILLY, BOUNCY, NUTTY, BOB;

    private Random rand = new Random(47);

    @Override

    public CartoonCharacter next() {

        return values()[rand.nextInt(values().length)];

    }

}

6. 随机选取

7. 使用接口组织枚举

子类化:implements是enum 子类化的唯一方法。

有时希望使用子类将一个enum中的元素进行分组。在一个接口内部创建实现该接口的枚举,以此将元素分组。

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 class TypeOfFood {

  public static void main(String[] args) {

    Food food = Appetizer.SALAD;

    food = MainCourse.LASAGNE;

    food = Dessert.GELATO;

    food = Coffee.CAPPUCCINO;

  }

}

8. 使用EnumSet替代位标志(BitSet)

Set:是一种集合不能添加重复元素。enum也要求其成员是唯一的。

EnumSet:Java SE5引入了EnumSet,是为了通过enum创建一种替代品,以替代传统的基于int的“位标志”(BitSet)。这种标志可以用来表示某种开关信息,不过,使用这种标志,最终操作的只是一些bit。

EnumSet的性能:它在说明一个二进制位是否存在时,具有更好的表达能力,并且无需担心性能。

EnumSet 包含的使用方法:

EnumSet.noneOf(AlarmPoints.class); 返回Empty set

EnumSet.complementOf() 用于创建包含与指定的Enum_Set类型相同的元素的EnumSet

EnumSet.of() 返回参数中添加的Enum元素集合

EnumSet.range() 返回参数中指定范围的Enum元素

points.addAll() 添加所有Enum元素

points.removeAl() 移除参数中包含的Enum元素集合

研究EnumSet文档,会发现of()方法被重载了很多次,不但为可变数量参数进行了重载,而且为接受2至5个显式的参数的情况都进行了重载。这也从侧面表现了EnumSet对性能的关注。

9. 使用EnumMap

EnumMap:是一种特殊的Map(Key-Value映射表),要求其中的键(key)必须来自于一个enum。

EnumMap的性能:由于enum本身的限制,所以EnumMap内部是数组实现。因此EnumMap速度很快,可以放心进行查找操作。

EnumMap的排序:与EnumSet一样,enum实例(key)定义时的次序决定了其在EnumMap中的顺序。

9.1 命令模式(GoF23之一)的具体实现

interface Command {

    void action();

}

enum AlarmPoints{

    STAIR,LOBBY,OFFICE,BATHROOM,UTILITY,KITCHEN

}

public class EnumMaps {

    public static void main(String[] args) {

        EnumMap<AlarmPoints, Command> em =

            new EnumMap<AlarmPoints, Command>(AlarmPoints.class);

        em.put(KITCHEN, new Command() {

            public void action() {

                print("Kitchen fire!");

            }

        });

        em.put(BATHROOM, new Command() {

            public void action() {

                print("Bathroom alert!");

            }

        });

        for (Map.Entry<AlarmPoints, Command> e : em.entrySet()) {

            printnb(e.getKey() + ": ");

            e.getValue().action();

        }

        try { // If there's no value for a particular key:

            em.get(UTILITY).action();

        } catch (Exception e) {

            print(e);

        }

    }

}

9.2 EnumMap 包含的使用方法:

put(k,v) 新增元素(k,v)

get(k) 返回参数k对应的value

entrySet() 返回<k,v>实例对象集合

Map.Entry<K,V>.getValue() 返回实例元素的value

10 常量相关的方法

常量相关的方法:constant-specific methods

多路分发:multiple dispatching

枚举元素:在Enum中定义的元素都是Enum类中的各个实例对象,每个Enum元素都是一个Enum类型的staic final类型对象。

常量相关的方法的enum实现方式:enum 允许为每个enum实例(枚举元素)编写方法,从而为每个enum实例赋予各自不同的行为,从而为每个enum实例赋予各自不同的行为。

public enum ConstantSpecificMethod {

    DATE_TIME {

        String getInfo() {

            return  DateFormat.getDateInstance().format(new Date());

        }

    },

    CLASSPATH {

        String getInfo() {

            return System.getenv("CLASSPATH");

        }

    },

    VERSION {

        String getInfo() {

            return System.getProperty("java.version");

        }

    };

    abstract String getInfo();

    public static void main(String[] args) {

        for (ConstantSpecificMethod csm : values()) {

            System.out.println(csm.getInfo());

        }

    }

}

10.1 使用enum的职责链

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

代码示例 参照原文P607

10.2 使用enum的状态机

状态机模式:(GoF23设计模式之一):一个状态机可以具有有限个特定的状态,它通常根据输入,从一个状态转移到下个状态,不过也可能存在瞬时状态(transient states),而一旦任务执行结束,状态机会立即离开瞬时状态。

瞬时状态:(transient states):

代码示例 参照原文P609

11 多路分发

单路分发:Java只支持单路分发:如果要执行的操作包含了不只一个类型未知的对象时,java的动态绑定机制只能处理其中一个的类型。

多路分发:含二路分发。

多路分发的几种实现方式:

使用enum分发:

使用常量相关的方法分发

使用EnumMap分发

使用二维数组

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容