在项目中,一般推荐使用枚举来代替常量接口和常量类。

但是,枚举类的用途不仅仅是定义常量,还有其它较多的方法,比如:实现接口定义抽象方法当作单例使用等。

本文是对Java枚举使用方法的一个整理,并给出示例。

在JDK 1.5之前,定义常量,我们一般使用两种方式:

  • 定义一个常量类
  • 定义一个常量接口

定义常量类,如:

/**
 * @author wangmengjun
 */
public class SeasonConstants {

    public static final int SPRING = 0;
    public static final int SUMMER = 1;
    public static final int FALL = 2;
    public static final int WINTER = 3;
}

定义常量接口,如:

/**
 * @author wangmengjun
 */
public interface SeasonInterface {

    public static final int SPRING = 0;
    public static final int SUMMER = 1;
    public static final int FALL = 2;
    public static final int WINTER = 3;
}

这样一来,凡是实现SeasonInterface接口的类都会自动继承这些常量

如:

public class Main implements SeasonInterface {

    public static void main(String[] args) {
        int season = SPRING;
    }
}

Java枚举类在JDK 1.5引入的,枚举类在项目中已经不可或缺。

正是因为Java枚举类可以有自定义的方法,可以实现接口、定义抽象类等,更加的灵活,已经被广大开发人员推荐在项目中使用 -- 使用枚举类替换接口常量或者类常量等。

接下来,我们一起来看一些使用枚举的示例:



Java枚举类使用

声明枚举的语法如下:

[public/protected/private] enum Enum_name {

... ...
}



无构造函数的枚举

我们可以定义一个Season的枚举,包含四个季节,如:

/**
 * @author wangmengjun
 */
public enum Season {

    SPRING,
    SUMMER,
    FALL,
    WINTER
}

每个枚举类的常量是 public、static、final修饰的。

可以结合switch来使用,如:

public class Main {

    public static void main(String[] args) {
        Season season = Season.SPRING;
        switch (season) {
            case SPRING:
                System.out.println("Spring ~~~");
                break;
            case SUMMER:
                System.out.println("Summer ~~~");
                break;
            case FALL:
                System.out.println("Fall ~~~");
                break;
            case WINTER:
                System.out.println("Winter ~~~");
                break;
            default:
                break;
        }
    }
}



有构造函数的枚举

构造函数中的参数可以一个或者多个。

  • 一个参数的构造函数示例

比如,美国硬币的种类,可以添加一个币值作为参数,如:

public enum Coin {

    PENNY(1),
    NICKEL(5),
    DIME(10),
    QUARTER(25); //US coins

    private int value;

    private Coin(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }
}

使用示例:

int coidValue = Coin.NICKEL.getValue(); //  5
  • 多个参数的构造函数示例:

有时候,我们需要不止一个参数,如一个产品类型的枚举类,可以包含2个参数,一个int值,一个String值,分别用于存入数据库的值和页面显示的值。

如:

public enum ProductType {

    SMART_HOME(0, "智能家居"),
    HEALTH_CARE(1, "医疗健康"),
    MOTION_DETECTION(2, "运动检测"),
    INDUSTRIAL_PRODUCTION(3, "工业生产"),
    ENVIRONMENT_MONITORING(4, "环境监测"),
    INTELLIGENT_OFFICE(6, "智能办公"),
    LOCATION_DEVICE(7, "定位器/防丢器"),
    SMART_GATEWAY(8, "智能网关"),
    OTHERS(5, "其它");

    private int code;

    private String name;

    private ProductType(int code, String name) {
        this.code = code;
        this.name = name;
    }

    /**
     * @return the code
     */
    public int getCode() {
        return code;
    }

    /**
     * @return the name
     */
    public String getName() {
        return name;
    }


}

这种情况,比较适合页面上下拉框选项的场景,如使用JSTL来循环一下传给页面的枚举类集:

<select class="form-control" id="J_pro_type" name="productType"
		data-change="click">
		<option value="">请选择产品类型</option>
	    <c:forEach items="${types}" var="type">
		   <option value="${type.code }"> ${type.name}</option>
		</c:forEach>

</select>

下拉框效果图如下:

java枚举值值翻译 java枚举用法_java枚举值值翻译



获取枚举类中所有常量

可以通过枚举类的values() 方法获取指定枚举的常量数组,如

Coin[] coins = Coin.values();

        for (Coin coin : coins) {
            System.out.println(String.format("%s --> value is %d", coin.name(), coin.getValue()));
        }

输出结果:

PENNY --> value is 1
NICKEL --> value is 5
DIME --> value is 10
QUARTER --> value is 25



包含自定义方法

我们可以在Coin枚举类中,添加一个自定的方法,比

public boolean isPenny() {
        return this == PENNY;
    }

修改后的Coin.java内容如下:

public enum Coin {

    PENNY(1),
    NICKEL(5),
    DIME(10),
    QUARTER(25); //US coins

    private int value;

    private Coin(int value) {
        this.value = value;
    }

    public boolean isPenny() {
        return this == PENNY;
    }

    public int getValue() {
        return value;
    }

}

这样,我们就能通过isPenny方法知道该Coin对象是否为PENNY。

Coin penny = Coin.PENNY;
       System.out.println(penny.isPenny());//true
       
       Coin dime = Coin.DIME;
       System.out.println(dime.isPenny());//false



实现接口

定义一个接口Next,包含nextSeason方法,用于输出下一个季节是什么。

public interface Next {
    void nextSeason();
}

修改Season枚举内容:

/**
 * @author wangmengjun
 */
public enum Season implements Next {

    SPRING,
    SUMMER,
    FALL,
    WINTER;

    @Override
    public void nextSeason() {
        switch (this) {
            case SPRING:
                System.out.println("Next season is Summer");
                break;
            case SUMMER:
                System.out.println("Next season is Fall");
                break;
            case FALL:
                System.out.println("Next season is Winter");
                break;
            case WINTER:
                System.out.println("Next season is Spring");
                break;
            default:
                break;
        }

    }
}

测试一下:

Season spring = Season.SPRING;
      spring.nextSeason();//输出Next season is Summer



包含抽象方法

除了可以实现接口外,枚举类还可以包含抽象方法

/**
 * @author wangmengjun
 */
public enum RegularExpressionEnum {

    NUMERIC("^[0-9]+$") {
        @Override
        public boolean match(String value) {
            return Pattern.matches(this.getRegPattern(), value);
        }
    },

    ALPHABETIC("^[a-zA-Z]+$") {
        @Override
        public boolean match(String value) {
            return Pattern.matches(this.getRegPattern(), value);
        }
    };

    private String regPattern;

    private RegularExpressionEnum(String regPattern) {
        this.regPattern = regPattern;
    }

    /**
     * @return the regPattern
     */
    public String getRegPattern() {
        return regPattern;
    }

    public abstract boolean match(String value);

}

示例:

RegularExpressionEnum numeric = RegularExpressionEnum.NUMERIC;
        System.out.println(numeric.match("123"));//true
        System.out.println(numeric.match("1a2"));//false

        RegularExpressionEnum alphabetic = RegularExpressionEnum.ALPHABETIC;
        System.out.println(alphabetic.match("123"));//false
        System.out.println(alphabetic.match("abc"));//true

如何选择,是选择实现接口或者使用抽象方法呢?

如果一个方法,每个枚举常量的方法实现都是一样的那么最好使用接口,不用抽象方法。实现接口,只要在枚举中实现一个接口方法即可;使用抽象方法,每个枚举中的常量都需要实现一遍抽象方法。

如果每个常量的行为各异,变化大,那么使用抽象方法来做,较为合适。

比如,将上述抽象方法的例子,改成实现接口的方式,一起来看一下代码的变化。

public interface RegularExpressionInterface {

    boolean match(String value);
}

RegularExpressionEnum修改如下:

/**
 * @author wangmengjun
 */
public enum RegularExpressionEnum implements RegularExpressionInterface {

    NUMERIC("^[0-9]+$"),
    ALPHABETIC("^[a-zA-Z]+$");

    private String regPattern;

    private RegularExpressionEnum(String regPattern) {
        this.regPattern = regPattern;
    }

    /**
     * @return the regPattern
     */
    public String getRegPattern() {
        return regPattern;
    }

    @Override
    public boolean match(String value) {
        return Pattern.matches(this.getRegPattern(), value);
    }
}

测试代码和结果和上述抽象方法的一样。

RegularExpressionEnum numeric = RegularExpressionEnum.NUMERIC;
        System.out.println(numeric.match("123"));//true
        System.out.println(numeric.match("1a2"));//false

        RegularExpressionEnum alphabetic = RegularExpressionEnum.ALPHABETIC;
        System.out.println(alphabetic.match("123"));//false
        System.out.println(alphabetic.match("abc"));//true



作为单例使用

public enum Attendant {

    INSTANCE;

    private Attendant() {
        // perform some initialization routine
    }

    public void sayHello() {
        System.out.println("Hello!");
    }
}


public class Main {

    public static void main(String[] args) {
        Attendant.INSTANCE.sayHello();// instantiated at this point
    }
}



包含静态变量和静态方法

以上面说过的产品类型(ProductType)枚举为例,int值存在数据库中,但是,需要在页面上根据int值显示对应的产品详细类型,这个时候我们可以在枚举类中添加一个Map, 然后添加一个静态方法getNameByCode,来实现,如:

private static final Map<Integer, ProductType> MAP = new HashMap<>();

    static {
        for (ProductType type : ProductType.values()) {
            MAP.put(type.getCode(), type);
        }
    }

 

public static String getTypeNameByCode(int code) {
        if (MAP.containsKey(code)) {
            return MAP.get(code).getName();
        }
        return "";
    }

完整的ProductType枚举内容如下:

import java.util.HashMap;
import java.util.Map;

public enum ProductType {

    SMART_HOME(0, "智能家居"),
    HEALTH_CARE(1, "医疗健康"),
    MOTION_DETECTION(2, "运动检测"),
    INDUSTRIAL_PRODUCTION(3, "工业生产"),
    ENVIRONMENT_MONITORING(4, "环境监测"),
    INTELLIGENT_OFFICE(6, "智能办公"),
    LOCATION_DEVICE(7, "定位器/防丢器"),
    SMART_GATEWAY(8, "智能网关"),
    OTHERS(5, "其它");

    private int code;

    private String name;

    private static final Map<Integer, ProductType> MAP = new HashMap<>();

    static {
        for (ProductType type : ProductType.values()) {
            MAP.put(type.getCode(), type);
        }
    }

    private ProductType(int code, String name) {
        this.code = code;
        this.name = name;
    }


    /**
     * 
     * @param code
     * @return
     */
    public static String getTypeNameByCode(int code) {
        if (MAP.containsKey(code)) {
            return MAP.get(code).getName();
        }
        return "";
    }

    /**
     * @return the code
     */
    public int getCode() {
        return code;
    }


    /**
     * @return the name
     */
    public String getName() {
        return name;
    }

}

这样一来,如果获取一个产品对象,其type为1,那么直接可以通过1获取对应的产品类型名称为医疗健康。可以设置到VO中,用于展示。

String name = ProductType.getTypeNameByCode(1);
		System.out.println(name);//医疗健康