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