enum和常量

今天遇到了一个问题,让我对enum以及常量有了更好的认识。
上回书说enum类型天生是final和static的。Java中的常量,我们知道也一般定义为static final。例如:

1
private static final int intConstant = 1;

上面这行代码中,变量intConstant就是一个常量,常量意味着intConstant的值在编译时就能够确定,并且无法改变。
这样听起来被定义为static final的变量就能称之为常量了啊,enum天生就是static final的,enum天生就是常量啦?

答案是,不一定哦~
发现问题
今天在使用mybatis写一个dao, 像之前每天做的工作一样,写一个简单的select语句。写的select语句中where子句中有个判断字段status,这个字段被定义为int,每一个int值对应一个状态。在以前,我会这样写这个sql语句。

1
2
@Select(Select column1, column2 from sample_table where status = 1 or status =2)
public List<SampleTable> getSampleData();

现在,我慢慢学会使用enum了,于是我打算把status的状态用enum来表示。于是我开始这样写代码。

1
2
3
4
5
6
7
8
9
private enum SAMPLE_STATUS{
STATUS1(1), STATUS2(2);
private int value;
private SAMPLE_STATUS(int value){
this.value = value
};
}
@Select(Select column1, column2 from sample_table where status = SAMPLE_STATUS.STATUS1.value or status =SAMPLE_STATUS.STATUS2.value)
public List<SampleTable> getSampleData();

可是,我这样写代码后@Select那行编译器报错: “attribute value must be constant“.

为什么啊,enum不是static final的吗?不应该是常量吗?为什么提示我说这个属性必须是常量呢?

分析问题
出现了这样的错误,我很好奇。重新看了下自己的代码,并进行了一番谷歌之后,我突然醒悟了。我tm太傻逼了。
真相应该是enum是天生static final没错,所以SAMPLE_STATUS.STATUS1是常量,但是,SAMPLE_STATUS.STATUS1对应的这个值就不一定是了。因为这个int值是在enum的构造器中初始化的,也就是说,SAMPLE_STATUS.STATUS1对应的int值,应该是在enum类被加载的时候才被赋值,编译时还没有被赋值。

为了验证我的想法,我写了下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private enum Fruit{
Strawberry(1), Banana(2), Kiwi(3);
private int value;
private Fruit(int value){
System.out.println("The value is " + value);
this.value = value;
}
}

public static void main(String [] args) {
int value = Fruit.Strawberry.value;
System.out.println("The value of strawberry is " + value);
Fruit.Strawberry.setValue(3);
System.out.print("The value of strawberry is " + value);
}

上面的代码运行的结果是:

1
2
3
4
5
The value is 1
The value is 2
The value is 3
The value of strawberry is 1
The value of strawberry is 1

前三行输出结果说明,当enum类加载的时候,会依次调用构造器,生成不同的枚举类实例,实例对应的int值也依次被初始化。
后两行输出结果说明,被初始化后的int值也是final的,不可被修改,尽管我调用了setValue方法,试图改变其int的值。

解决问题
既然enum对应的int值不是常量,我还是想用enum类型去表示我的sql语句中的status字段的值,咋办呢?

1
2
3
4
5
6
7
8
9
@SelectProvider(type = Provider.class, method = "getSampleData")
public List<SampleTable> getSampleData();

public static class Provider {
public static String getSampleData(){

return "Select column1, column2 from sample_table where status =" + SAMPLE_STATUS.STATUS1.value + "or status =" + SAMPLE_STATUS.STATUS2.value;
}
}

注解中要求所有的值必须是常量,但是class中没有这样的要求。
所以,使用Provider可以完美的解决这个问题。