在项目中,一般推荐使用枚举来代替常量接口和常量类。
但是,枚举类的用途不仅仅是定义常量,还有其它较多的方法,比如:实现接口、定义抽象方法、当作单例使用等。
本文是对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>
下拉框效果图如下:
获取枚举类中所有常量
可以通过枚举类的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);//医疗健康
大家如果对枚举类的使用,还有其它使用方法也请留言给出,大家一起学习分享~~ :)