Java Enum、EnumMap、EnumSet 详解

除不能 继承 java.lang.Enum  之外,可将其看做一个常规类,甚至可以有main方法

1> 常量
把相关的常量分组到一个枚举类型里,且枚举提供 比常量更多的方法
public enum Color {
    RED , GREEN , BLANK , YELLOW
}

2>  switch
switch语句只支持int, char, enum类型,使用枚举,能让我们的代码可读性更强
enum Signal {
    GREEN , YELLOW , RED
}

public class TrafficLight {
    Signal color = Signal. RED ;
    public void change() {
        switch ( color ) {
            case RED :
                color = Signal. GREEN ;
                break ;
            case YELLOW :
                color = Signal. RED ;
                break ;
            case GREEN :
                color = Signal. YELLOW ;
                break ;
        }
    }
}

3> 向枚举中添加新方法
自定义自己的方法,必须在 enum实例序列的最后添加一个分号,而且 Java 要求必须先定义 enum 实例,成员变量和枚举中的数据类型的顺序一一对应
如:回对实例自身的描述,而非默认的 toString 返回枚举实例的名字
public enum Color {
    RED ( "红色" , 1 ), GREEN ( "绿色" , 2 ), BLANK ( "白色" , 3 ), YELLO ( "黄色" , 4 );
    // 成员变量
    private String name ;
    private int index ;

    // 构造方法
    private Color(String name, int index) {
        this . name = name;
        this . index = index;
    }
    // 普通方法
    public static String getName( int index) {
        for (Color c : Color. values ()) {
            if (c.getIndex() == index) {
                return c. name ;
            }
        }
        return null ;
    }
    // get set 方法
    public String getName() {
        return name ;
    }
    public void setName(String name) {
        this . name = name;
    }
    public int getIndex() {
        return index ;
    }
    public void setIndex( int index) {
        this . index = index;
    }
}
注 : 1> 可以调用相应枚举成员的方法来生成相应的对象,比如下面的 OFType,可以这样使用:
            OFType t = OFType.HELLO;
            t.newInstance();
       2> 构造方法的 访问权限 不能是 public

4> 覆盖枚举的方法
下面给出一个toString()方法覆盖的例子
public class Test {
    public enum Color {
        RED ( "红色" , 1 ),
        GREEN ( "绿色" , 2 ),
        BLANK ( "白色" , 3 ),
        YELLO ( "黄色" , 4 );

        // 成员变量
        private String name ;
        private int index ;

        // 构造方法
        private Color(String name, int index) {
            this . name = name;
            this . index = index;
        }
        // 覆盖方法
        @Override
        public String toString() {
            return this . index + "_" + this . name ;
        }
    }

    public static void main(String[] args) {
        System. out .println(Color. RED .toString());
    }
}
注 : 所有的方法或其它必须放在枚举值的下面,且枚举的构造方法必须是 private

5> 实现接口
所有的枚举都继承自 java.lang.Enum类,由于Java 不支持多继承,所以枚举对象不能再继承其他类
public interface Behaviour {
    void print();
    String getInfo();
}

public enum Color implements Behaviour {
    RED ( "红色" , 1 ),
    GREEN ( "绿色" , 2 ),
    BLANK ( "白色" , 3 ),
    YELLO ( "黄色" , 4 );

    // 成员变量
    private String name ;
    private int index ;

    // 构造方法
    private Color(String name, int index) {
        this . name = name;
        this . index = index;
    }

    // 接口方法
    @Override
    public String getInfo() {
        return this . name ;
    }

    // 接口方法
    @Override
    public void print() {
        System. out .println( this . index + ":" + this . name );
    }
}

6> 使用接口组织枚举
public interface Food {
    enum Coffee implements Food {
        BLACK_COFFEE ,
        DECAF_COFFEE ,
        LATTE ,
        CAPPUCCINO
    }

    enum Dessert implements Food {
        FRUIT , CAKE , GELATO
    }
}

7> 关于枚举集合的使用
java.util.EnumSet 和 java.util.EnumMap 是两个枚举集合,EnumSet保证集合中的元素不重复;EnumMap中的 key是enum类型,而 value 则可以是任意类型

枚举和常量定义的区别
<1> 通常定义常量方法
通常利用 public final static方法 定义的代码如下,分别用1表示红灯,3表示绿灯,2表示黄灯
public class Light {
    /* 红灯 */
    public final static int RED = 1 ;
    /* 绿灯 */
    public final static int GREEN = 3 ;
    /* 黄灯 */
    public final static int YELLOW = 2 ;
}

<2> 枚举类型定义常量方法
枚举类型的简单定义方法如下,似乎没办法定义每个枚举类型的值。比如定义红灯、绿灯和黄灯的代码可能如下 :
public enum Light { 
    RED, 
    GREEN, 
    YELLOW; 
 }
只能够表示出红灯、绿灯和黄灯,但是具体的值没办法表示出来。枚举类型提供了构造函数,可以通过构造函数和覆写toString方法来实现。首先给Light枚举类型增加构造方法,然后每个枚举类型的值通过构造函数传入对应的参数,同时覆写toString方法,在该方法中返回从构造函数中传入的参数,改造后的代码如下 :
public enum Light {
    // 利用构造函数传参
    RED ( 1 ), GREEN ( 3 ), YELLOW ( 2 );

    // 定义私有变量
    private int nCode ;

    // 构造函数,枚举类型只能为私有
    private Light( int _nCode) {
        this . nCode = _nCode;
    }

    @Override
    public String toString() {
        return String. valueOf ( this . nCode );
    }
}

<3> 完整示例代码
枚举类型的完整演示代码如下 :
public class LightTest {
    // 1.定义枚举类型
    public enum Light {
        // 利用构造函数传参
        RED ( 1 ), GREEN ( 3 ), YELLOW ( 2 );

        // 定义私有变量
        private int nCode ;

        // 构造函数,枚举类型只能为私有
        private Light( int _nCode) {
            this . nCode = _nCode;
        }

        @Override
        public String toString() {
            return String. valueOf ( this . nCode );
        }
    }

    public static void main(String[] args) {
        // 1.遍历枚举类型
        System. out .println( "演示枚举类型的遍历 ......" );
        testTraversalEnum ();
        // 2.演示EnumMap对象的使用
        System. out .println( "演示EnmuMap对象的使用和遍历....." );
        testEnumMap ();
        // 3.演示EnmuSet的使用
        System. out .println( "演示EnmuSet对象的使用和遍历....." );
        testEnumSet ();
    }

    /** 演示枚举类型的遍历 */
    private static void testTraversalEnum() {
        Light[] allLight = Light. values ();
        for (Light aLight : allLight) {
            System. out .println( "当前灯name:" + aLight.name()); // name() 是对应的大写字母 如:RED,GREEN,YELLOW
            System. out .println( "当前灯ordinal:" + aLight.ordinal()); // ordinal() 是对应的索引号 基于0
            System. out .println( "当前灯:" + aLight);
        }
    }

    /** 演示EnumMap的使用,EnumMap跟HashMap的使用差不多,只不过key要是枚举类型 */
    private static void testEnumMap() {
        // 1.演示定义EnumMap对象,EnumMap对象的构造函数需要参数传入,默认是key的类的类型
        EnumMap currEnumMap = new EnumMap(Light. class );
        currEnumMap.put(Light. RED , "红灯" );
        currEnumMap.put(Light. GREEN , "绿灯" );
        currEnumMap.put(Light. YELLOW , "黄灯" );

        // 2.遍历对象
        for (Light aLight : Light. values ()) {
            System. out .println( "[key=" + aLight.name() + ",value=" + currEnumMap.get(aLight) + "]" );
        }
    }

    /** 演示EnumSet如何使用,EnumSet是一个抽象类,获取一个类型的枚举类型内容,可以使用allOf方法 */
    private static void testEnumSet() {
        EnumSet currEnumSet = EnumSet. allOf (Light. class );
        for (Light aLightSetElement : currEnumSet) {
            System. out .println( "当前EnumSet中数据为:" + aLightSetElement);
        }
    }
}
执行结果如下:
演示枚举类型的遍历 ......
当前灯name:RED
当前灯ordinal:0
当前灯:1
当前灯name:GREEN
当前灯ordinal:1
当前灯:3
当前灯name:YELLOW
当前灯ordinal:2
当前灯:2
演示EnmuMap对象的使用和遍历.....
[key=RED,value=红灯]
[key=GREEN,value=绿灯]
[key=YELLOW,value=黄灯]
演示EnmuSet对象的使用和遍历.....
当前EnumSet中数据为:1
当前EnumSet中数据为:3
当前EnumSet中数据为:2

<4> 通常定义常量方法和枚举定义常量方法区别
1. 代码 :
public class State { 
    public static final int ON = 1; 
    public static final Int OFF = 0; 
}
这样写有缺陷 :
         <1> 不是类型安全的。你必须确保是int
         <2> 要确保它的范围是 0 和 1
         <3> 很多时候你打印出来的时候,你只看到 1 和 0 , 没有看到代码的人并不知道你的企图,抛弃你所有旧的public static final常量

2. 可以创建一个enum类,把它看做一个普通的类。除了它不能继承其他类了 (java是单继承,它已经继承了Enum)
可以添加其他方法,覆盖它本身的方法

3. switch()参数可以使用enum了

4. values()方法是编译器插入到enum定义中的static方法,所以,当你将enum实例向上转型为父类Enum是,values()就不可访问。解决办法:在Class中有一个getEnumConstants()方法,所以即便Enum接口中没有values()方法,我们仍然可以通过Class对象取得所有的enum实例

5. 无法从enum继承子类,如果需要扩展enum中的元素,在一个接口的内部,创建实现该接口的枚举,以此将元素进行分组。达到将枚举元素进行分组

6. 使用EnumSet代替标志。enum要求其成员都是唯一的,但是enum中不能删除添加元素

7. EnumMap的key是enum,value是任何其他Object对象

8. enum允许程序员为eunm实例编写方法。所以可以为每个enum实例赋予各自不同的行为

9. 使用enum的职责链(Chain of Responsibility) .这个关系到设计模式的职责链模式。以多种不同的方法来解决一个问题。然后将他们链接在一起。当一个请求到来时,遍历这个链,直到链中的某个解决方案能够处理该请求

10. 使用enum的状态机

11. 使用enum多路分发

12.enum中的元素不能以数字开头,而常量定义方法可以

EnumSet
用来操作 Enum的集合,是一个抽象类,它有两个继承类 : JumboEnumSet 和 RegularEnumSet。其中包含的数据类型必须是 继承自Enum,由于 每次add的时候,每个枚举值只占一个长整型的一位,因此其操作速度特别快

EnumMap
Map接口的实现,其key-value映射中的key是Enum类型, 效率比HashMap高,可以直接获取数组下标索引并访问到元素

测试案例 :
public class EnumTest {
    public enum PictureType {
        BMP ( "bmp" , 0 ) {
            @Override
            public String getInfo() {
                return "这是bmp格式图片" ;
            }
            public void getPrint() { // 没有覆盖的方法无法直接访问
                System. out .println( "测试添加其他方法" );
            }
        },
        JPG ( "jpg" , 1 ) {
            @Override
            public String getInfo() {
                return "这是jpg格式图片" ;
            }
        },
        JPEG ( "jpeg" , 2 ) {
            @Override
            public String getInfo() {
                return "这是jpeg格式图片" ;
            }
        },
        PNG ( "png" , 3 ) {
            @Override
            public String getInfo() {
                return "这是png格式图片" ;
            }
        },
        GIF ( "gif" , 4 ) {
            @Override
            public String getInfo() {
                return "这是gif格式图片" ;
            }
        };

        private String name ;
        private int index ;

        /**
         * 获取信息
         *
         * @return
         */
        public String getInfo() {
            return "" ;
        }

        // 构造函数不能是 public
        private PictureType(String name, int index) {
            this . name = name;
            this . index = index;
        }

        public String getName() {
            return name ;
        }

        public void setName(String name) {
            this . name = name;
        }

        public int getIndex() {
            return index ;
        }

        public void setIndex( int index) {
            this . index = index;
        }

        public static String getName( int index) {
            for (PictureType c : PictureType. values ()) {
                if (c.getIndex() == index) {
                    return c. name ;
                }
            }
            return null ;
        }

        public static PictureType getPictureType(String name) {
            for (PictureType c : PictureType. values ()) {
                if (c.getName().equals(name)) {
                    return c;
                }
            }
            return null ;
        }

        @Override
        public String toString() {
            return "name=" + this .getName() + ";index=" + this . index ;
        }
    }

    public static void main(String[] args) {
        // 访问其中某一值
        System. out .println(PictureType. BMP .getName() + " => " + PictureType. BMP .getIndex());
        System. out .println(PictureType. GIF .getName() + " => " + PictureType. GIF .getIndex());
        // 遍历
        PictureType[] all = PictureType. values ();
        StringBuilder sb;
        for (PictureType a : all) {
            sb = new StringBuilder();
            sb.append( "name:" ).append(a.getName())
                    .append( " index:" ).append(a.getIndex())
                    .append( " ordinal:" ).append(a.ordinal()) // 系统给出对应的索引号 基于0
                    .append( " 描述信息:" ).append(a.getInfo());
            System. out .println(sb.toString());
        }
        // 修改其中的值,可以不提供 set 方法来防止数据被修改
        PictureType. GIF .setName( "嘻嘻哈哈" );
        System. out .println(PictureType. GIF + " => " + PictureType. GIF .getName());

        // EnumSet
        EnumSet ofSets = EnumSet. of (PictureType. BMP ); // of 创建一个最初包含指定元素的枚举 set
        ofSets.add(PictureType. JPG );
        for (PictureType en : ofSets) {
            System. out .println( "of => " + en.toString());
        }
        EnumSet allSets = EnumSet. allOf (PictureType. class ); // allOf 创建一个包含指定元素类型的所有元素的枚举 set
        for (PictureType en : allSets) {
            System. out .println( "allOf => " + en.toString());
        }
        EnumSet rangeSets = EnumSet. range (PictureType. BMP , PictureType. JPEG ); // 创建一个指定范围 set
        for (PictureType en : rangeSets) {
            System. out .println( "range => " + en.toString());
        }
        EnumSet noneSets = EnumSet. noneOf (PictureType. class ); // 创建一个指定枚举类型的空set
        noneSets.add(PictureType. BMP );
        noneSets.add(PictureType. JPG );
        noneSets.add(PictureType. JPEG );
        Iterator iterable = noneSets.iterator();
        while (iterable.hasNext()) {
            System. out .println( "noneOf => " + iterable.next().toString());
        }

        // EnumMap 其中的 key 是 Enum 类型因此操作速度比 HashMap 速度快,其才做方法和 HashMap 基本一样
        Map map = new EnumMap<>(PictureType. class );
        map.put(PictureType. BMP , "这是一个 BMP 格式图片" );
    }
}

enum 与 int、String 之间的转换
enum 的 name()方法,可以将  enum 元素转换成字符串,再通过  valueOf() 方法将 enum元素名称转换成 enum元素
String : enumType.name()
enumType :  enum.valueOf(name)
int:enumType.value.ordinal()
enum : enum.values()[i]

你可能感兴趣的:(Java)