枚举
为什么需要枚举
类常量不好用么?为什么要使用枚举。拿我之前的代码来做例子:
酒店订单的状态公有7种:已创建、支付超时、已取消、待商家确认、待入住、商家取消订单、已消费,我给它们都用一个类常量表示
class OrderStatus
{
public static final int CRETEA = 1;
public static final int TIMEOUT = 10;
public static final int CANCELED = 20;
public static final int WAIT_CONFIRM = 30;
public static final int WAIT_COME = 40;
public static final int BUSINESS_CANCELED = 50;
public static final int CONSUMED = 60;
}
对于这种单值类型的静态常量定义,本身也没错,主要是在使用的地方没有一个明确性的约束而已,比如:
/*
* 修改订单状态
*/
public bool setStatus (int orderId, int orderStatus)
{
// ...
}
这里的orderStatus,我们初意是传入OrderStatus类常量,但是由于没有类型约束,传入任意一个int值都是可以的,编译器也不会发出任何警告。
现在,我们用枚举类来修改上述代码:
enum OrderStatus{
CRETEA,TIMEOUT,CANCELED,WAIT_CONFIRM,WAIT_COME,BUSINESS_CANCELED,CONSUMED
}
给setStatus改成更严格的类型约束:
public boolean setStatus (int orderId, OrderStatus status)
{
return true;
}
自定义枚举类
好了,我们已经知道了枚举是很有作用的。接下来开始学习如何使用枚举类了。
在jdk1.5之前,是没有enum关键字的,需要自定义枚举类。会写自定义枚举类,对照学习enum就非常轻松。
自定义枚举类过程:
- 私有化构造器,类外不能创建其对象
- 在类的内部创建枚举类的实例。声明为:public static final
- 对象如果有实例属性,应该声明为private final,并在构造器中初始化
下面我们来创建两个枚举类,一个含属性,一个不含属性
第一个是关于星期的枚举类:
class Weekend
{
public static final Weekend MON = new Weekend();
public static final Weekend TUS = new Weekend();
public static final Weekend WED = new Weekend();
public static final Weekend THU = new Weekend();
public static final Weekend FRI = new Weekend();
public static final Weekend SAT = new Weekend();
public static final Weekend SUN = new Weekend();
private Weekend ()
{
}
}
第二个是关于订单状态的枚举类:
class OrderStatus
{
private final int val;
private final String desc;
public static final OrderStatus CREATE = new OrderStatus(10, "订单已创建");
public static final OrderStatus TIMEOUT = new OrderStatus(20, "支付超时");
public static final OrderStatus PAYED = new OrderStatus(30, "已支付");
public static final OrderStatus REFUND = new OrderStatus(40, "已退款");
private OrderStatus (int value, String d)
{
val = value;
desc = d;
}
public int getVal() {
return val;
}
public String getDesc() {
return desc;
}
@Override
public String toString() {
return "OrderStatus{" +
"val=" + val +
", desc='" + desc + '\'' +
'}';
}
}
测试代码如下:
OrderStatus[] arrs = {OrderStatus.CREATE ,OrderStatus.TIMEOUT,OrderStatus.PAYED,OrderStatus.REFUND };
for (OrderStatus o : arrs) {
System.out.println(o);
}
使用enum关键字定义枚举类
使用enum关键字后,就可以简化我们的操作,定义枚举类就变得非常方便。
enum Week { MON ,TUS,WED,THU,FRI,SAT,SUN}
enum OrderStatus
{
CREATE(10, "订单已创建"),
TIMEOUT(20, "支付超时"),
PAYED(30, "已支付"),
REFUND(40, "已退款");
private final int val;
private final String desc;
private OrderStatus (int value, String d)
{
val = value;
desc = d;
}
public int getVal() {
return val;
}
public String getDesc() {
return desc;
}
@Override
public String toString() {
return "OrderStatus{" +
"val=" + val +
", desc='" + desc + '\'' +
'}';
}
}
// 测试代码
OrderStatus[] arrs = {OrderStatus.CREATE, OrderStatus.TIMEOUT, OrderStatus.PAYED, OrderStatus.REFUND};
for (OrderStatus o : arrs) {
System.out.println(o.getDesc());
}
enum特点和常用方法
有以下几个特点:
- 定义的
enum
类型总是继承自java.lang.Enum
,且无法被继承; - 只能定义出
enum
的实例,而无法通过new
操作符创建enum
的实例; - 定义的每个实例都是引用类型的唯一实例;
- 可以将
enum
类型用于switch
语句。
编译后的enum
类和普通class
并没有任何区别。但是我们自己无法按定义普通class
那样来定义enum
,必须使用enum
关键字,这是Java语法规定的。
因为enum
是一个class
,每个枚举的值都是class
实例,因此,这些实例有一些方法:
values()
返回枚举类型的对象数组。该方法可以很方便地遍历所有的枚举值。
OrderStatus[] arrs = OrderStatus.values();
name()
name:返回当前枚举类对象常量的名称。默认情况下,toString方法和name方法返回值是一样的。但是,name方法不会被重写,toString方法可以。所以,想要获取枚举类对象常量的名称,必须使用name方法。
注意:判断枚举常量的名字,要始终使用name()方法,绝不能调用toString()!
OrderStatus[] arrs = OrderStatus.values();
for (OrderStatus o :arrs){
System.out.println(o.name());
}
// 依次打印CREATE TIMEOUT PAYED REFUND
valueOf()
valueOf(String str):可以把一个字符串转为对应的枚举类对象。要求字符串必须是枚举类对象的“名字”。如不是,会有运行时异常:IllegalArgumentException。
String createStr = "CREATE";
OrderStatus create = OrderStatus.valueOf(createStr);
System.out.println(create.getDesc()); // 订单已创建
enum的比较
使用enum
定义的枚举类是一种引用类型。前面我们讲到,引用类型比较,要使用equals()
方法,如果使用==
比较,它比较的是两个引用类型的变量是否是同一个对象。因此,引用类型比较,要始终使用equals()
方法,但enum
类型可以例外。
这是因为enum
类型的每个常量在JVM中只有一个唯一实例,所以可以直接用==
比较:
if (day == Weekday.FRI) { // ok!
}
if (day.equals(Weekday.SUN)) { // ok, but more code!
}
switch中使用enum
枚举类可以应用在switch
语句中。因为枚举类天生具有类型信息和有限个枚举常量,所以比int
、String
类型更适合用在switch
语句中:
public class Main {
public static void main(String[] args) {
Weekday day = Weekday.SUN;
switch(day) {
case MON:
case TUE:
case WED:
case THU:
case FRI:
System.out.println("Today is " + day + ". Work at office!");
break;
case SAT:
case SUN:
System.out.println("Today is " + day + ". Work at home!");
break;
default:
throw new RuntimeException("cannot process " + day);
}
}
}
enum Weekday {
MON, TUE, WED, THU, FRI, SAT, SUN;
}
枚举与接口
和普通 Java 类一样,枚举类可以实现一个或多个接口。若每个枚举值在调用实现的接口方法呈现相同的行为方式,则只要统一实现该方法即可。若需要每个枚举值在调用实现的接口方法呈现出不同的行为方式,则可以让每个枚举值分别来实现该方法。
interface IPay
{
void pay();
}
enum PayType implements IPay
{
WXPAY("微信支付") {
@Override
public void pay() {
System.out.println("微信支付成功");
}
},
ALIPAY("支付宝支付") {
@Override
public void pay() {
System.out.println("支付宝支付成功");
}
};
private final String desc;
private PayType (String desc)
{
this.desc = desc;
}
@Override
public String toString() {
return desc;
}
}
测试代码如下:
public class T2 {
public static void main(String[] args) {
PayType[] paytypes = PayType.values();
for (PayType t : paytypes) {
System.out.print(t + ":");
t.pay();
}
}
}
输入内容如下:
微信支付:微信支付成功
支付宝支付:支付宝支付成功