在项目中,一般推荐使用枚举来代替常量接口和常量类。但是,枚举类的用途不仅仅是定义常量,还有其它较多的方法,比如:实现接口、定义抽象方法、当作单例使用等。
本文是对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枚举类可以有自定义的方法,可以实现接口、定义抽象类等,更加的灵活,已经被广大开发人员推荐在项目中使用 -- 使用枚举类替换接口常量或者类常量等。
接下来,我们一起来看一些使用枚举的示例:
声明枚举的语法如下:
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 1NICKEL --> value is 5DIME --> value is 10QUARTER --> 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
1.7 作为单例使用
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);//医疗健康
大家如果对枚举类的使用,还有其它使用方法也请留言给出,大家一起学习分享~~ :)