java枚举类型学习

Posted Ryan Miao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java枚举类型学习相关的知识,希望对你有一定的参考价值。

用的不多,但用的时候仅仅简单的使用,不太明白原理,今天就系统的学一下枚举。参考:java编程思想。

1.简单创建

枚举就是一个固定的集合,内容是声明的类。

技术分享
package com.test.java.tenum;

/**
 * 一个简单的enum实例
 * Created by Administrator on 2016/3/30.
 */
public enum SimpleEnumUse {
    NOT,MILD,MEDIUM,HOT,FLAMING
}

class TestSE{
    public static void main(String[] args) {
        SimpleEnumUse medium = SimpleEnumUse.MEDIUM;
        System.out.println(medium);
        System.out.println(medium.ordinal());
    }
}
View Code

创建enum的时候,里面的元素就是类,默认创建了toString(),odinal()方法以及value字段。其中odinal是声明的顺序(从0开始),value就是声明的名字。简单的使用的话,这样就可以了,配合switch即可。

2.深入了解

2.1基本特性

创建enum时,编译器会为你生成一个相关的类,这个类继承自java.lang.Enum。下面实例:

技术分享
package com.test.java.tenum;

/**
 * 了解enum特性
 * Created by Administrator on 2016/3/30.
 */
public class EnumClass {
    public static void main(String[] args) {
        for (Shrubbery s : Shrubbery.values()) {
            //s继承Enum
            //ordinal表示声明的顺序
            System.out.print(s+" ordinal:"+s.ordinal());
            //enum自动实现了Comparable接口
            System.out.println(" compareTo:"+s.compareTo(Shrubbery.CRAWING));
            //编译器提供了equals和hashCode
            System.out.println(s.equals(Shrubbery.CRAWING));
            System.out.println(s==Shrubbery.CRAWING);
            System.out.println(s.getDeclaringClass());
            System.out.println(s.name());
            System.out.println("======================");
        }
        for (String s :
                "HANGING CRAWING GROUND".split(" ")) {
            Shrubbery shrubbery = Enum.valueOf(Shrubbery.class, s);
            System.out.println(shrubbery);

        }
    }
}

enum Shrubbery{
    GROUND,CRAWING,HANGING
}
View Code

ordinal()方法返回一个int值,这个每个enum实例在声明时的次序,从0开始。可以使用==比较enum实例,编译器自动为你提供了equals()和hashCode()方法。Enum类实现了Comparable接口和Serializable接口。

2.1.1静态导入

在使用enum的实例的时候发现人家不是EnumClass.INSTANCE,即不是通过类名调用的,而是直接调用,看了下。原来是在包下静态导入了。即:

import static package.EnumClass.*。

究竟显示的修饰enum实例还是静态导入要看代码的复杂度,看看静态导入是不是会让代码难以理解。

2.2可以在enum中添加自己的方法

enum可以添加方法和构造器。

技术分享
package com.test.java.tenum;

/**
 * 可以在enum中添加方法和构造器
 * Created by Administrator on 2016/3/30.
 */
public enum OzWitch {
    WEST("Miss Gulch, aka the Wiched 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;
    private OzWitch(String description){
        this.description = description;
    }
    public String getDescription(){
        return description;
    }

    public static void main(String[] args) {
        for (OzWitch with :
                OzWitch.values()) {
            System.out.println(with+":"+with.getDescription());
        }
    }
}
View Code

通常,将enum的构造方法声明private,而实际上对于它的可访问性来说没有什么变化,因为即使不是private也只能在enum内部使用创建enum实例。一旦enum定义结束,编译器就不允许我们再使用其构造器来创建任何实例了。另外,必须优先声明实例,然后声明方法或属性,否则编译报错

2.3探究values()

经过一下检测,values是编译器添加到你创建的enum类的,而Enum类本身中并没有values方法。我们可以通过Class对象获取所有的enum实例。

技术分享
package com.test.java.tenum;

import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Set;
import java.util.TreeSet;

/**
 * 通过反射查看enum的values
 * 结果:values是编译器添加到enum类的,而Enum类本身没有values方法
 * Created by Administrator on 2016/3/30.
 */
public class Reflection {
    public static Set<String> analyze(Class<?> enumClass){
        System.out.println("-----------Analyzing "+enumClass+"-------");
        System.out.println("Interfaces:");
        for (Type t :
                enumClass.getGenericInterfaces()) {
            System.out.println(t);
        }
        System.out.println("Base: "+enumClass.getSuperclass());
        System.out.println("Method:");
        Set<String> methods = new TreeSet<>();
        for (Method m :
                enumClass.getMethods()) {
            methods.add(m.getName());
        }
        System.out.println(methods);
        return methods;
    }
    public static void main(String[] args){
        Set<String> exploreMethods = analyze(Explore.class);
        Set<String> enumMethods = analyze(Enum.class);
        System.out.println("Explore.containsAll(Enum)?"+enumMethods.containsAll(enumMethods));
        System.out.println("Explore.removeAll(Enum):");
        exploreMethods.removeAll(enumMethods);
        System.out.println(exploreMethods);

    }
}
enum Explore{
    HERE,THERE
}
/************************通过class获取实例********************************/
enum Search{
    HITHER,YON
}
class UpcastEnum{
    public static void main(String[] args) {
        Search[] values = Search.values();
        Enum e = Search.HITHER;
//        e.values();//编译报错,说明No values() in Enum
        for (Enum anEnum : e.getClass().getEnumConstants()) {
            System.out.println(anEnum);
        }
    }
}

class NonEnum{
    public static void main(String[] args) {
        Class<Integer> integerClass = Integer.class;
        try{
            for (Object en : integerClass.getEnumConstants()) {
                System.out.println(en);
            }
        }catch (Exception e){
            System.out.println(e);
        }
    }
}
View Code

2.4通过实现接口而不是继承他类来扩展

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

2.5随机选取

技术分享
package com.test.java.tenum;

import java.util.Random;

/**
 * 随机抽取的工具类
 * Created by Administrator on 2016/3/30.
 */
public class Enums {
    private static Random random = 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[random.nextInt(values.length)];
    }
}
View Code

2.6使用接口组织枚举

无法从enum继承子类有时很令人沮丧。这种需求有时源自我们希望扩展原enum中的元素,有时我们希望使用子类将一个enum中的元素进行分组。

在一个接口内部创建实现该接口的枚举,以此将元素进行分组,可以达到将枚举元素分类的目的。举例来说,假设想用enum表示不同的食物,同时还希望每个enum元素仍然保持Food类型。可以这样:

技术分享
package com.test.java.tenum;

/**
 * 使用接口组织枚举
 * Created by Administrator on 2016/3/30.
 */
public interface Food {
    //开胃食物
    enum Appetizer implements Food{
        SALAD,SOUP,SPRING_ROLLS;
    }
    //主菜
    enum MainCourse implements Food{
        LASNGE,BURRITO,PAD_THAI,LENTILS,HUMMOUS,VINDALOO;
    }
    //甜点
    enum Dessert implements Food{
        TIRAMISU,GELATO,BLACK_FOREST_CAKE,FRUIT,CREME_CARMEL;
    }
    //coffe
    enum Coffee implements Food{
        BLACK_COFFEE,DECAF_COFFEE,ESPRESSO,LATTE,CAPPUCCINO,TEA,HERB_TEA;
    }
    //....
}
class TypeOfFood{
    public static void main(String[] args) {
        Food food = Food.Appetizer.SALAD;
        food = Food.MainCourse.PAD_THAI;
    }
    
}
View Code

如果enum类型实现了Food接口,那么我们就可以将其实例向上转型为Food,所以上例中的所有东西都是Food。

然而当你需要与一大堆类型打交道时,接口就不如enum好用。例如,你想创建一个枚举的枚举。那么可以创建一个新的enum,然后用其实例包装Food中的每一个enum类。

技术分享
package com.test.java.tenum;

/**
 * 枚举的枚举
 * Created by Administrator on 2016/3/30.
 */
public enum Course {
    APPETIZER(Food.Appetizer.class),
    MAINCOURSE(Food.MainCourse.class);

    private Food[] values;
    private Course(Class<? extends Food> kind){
        values = kind.getEnumConstants();
    }
    public Food randomSelection(){
        return Enums.random(values);
    }
}
View Code

在上面的程序中,每个Course实例都将其对应的Class对象作为构造器的参数。通过getEnumConstants方法,可以从该Class对象中取得某个Food子类的所有enum实例。因此,我们通过随机可以生成一份菜单:

技术分享
class Meal{
    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            for (Course course : Course.values()) {
                Food food = course.randomSelection();
                System.out.println(food);
            }
            System.out.println("-------");
        }
    }
}
View Code

在这个例子中,我们通过遍历每个Course实例来获得枚举的枚举的值。此外,还可以更简洁:将一个enum嵌套在另一个enum内。

技术分享
package com.test.java.tenum;

/**
 * 枚举嵌套
 * Created by Administrator on 2016/3/30.
 */
public enum SecurityCategory {
    STOCK(Security.Stock.class),
    BOND(Security.Bond.class);

    Security[] values;
    SecurityCategory(Class<? extends Security> kind){
        values = kind.getEnumConstants();
    }
    interface Security{
        enum Stock implements Security{SHORT,LONG,MARGIN}
        enum Bond implements Security{MUNICIPAL,JUNK}
    }
    public Security randomSelection(){
        return Enums.random(values);
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            SecurityCategory random = Enums.random(SecurityCategory.class);
            System.out.println(random+":"+random.randomSelection());
        }
    }
}
View Code

Security接口的作用是将其所包含的enum组成一个公共类型,这一点是必要的。然后SecurityCategory才能将Security中的enum作为其构造器的参数作用,以起到组织的效果。

 

以上是关于java枚举类型学习的主要内容,如果未能解决你的问题,请参考以下文章

马凯军201771010116《面向对象程序设计(java)》第六周学习总结

掌握Java枚举这几个知识点,日常开发就够啦

Java 枚举(enum)的用法

Java 枚举(enum)的用法

Java 数据类型在实际开发中应用二枚举

Java数据类型在实际开发中的应用二枚举类型