近日,在看Enum的定义时,看到其声明为
public abstract class Enum<E extends Enum<E>> implements Comparable<E>
暂时忽略Constable和Serializable
那么,为什么会这样声明呢?
先抛弃java中的定义,咱们自己先定义一个Enum,看看在自定义过程中会发生什么。
第一版
public abstract class CustomEnum implements Comparable<CustomEnum> {
private final String name;
private final int ordinal;
protected CustomEnum(String name, int ordinal) {
this.name = name;
this.ordinal = ordinal;
}
public final String name() {
return name;
}
public final int ordinal() {
return ordinal;
}
@Override
public int compareTo(@NotNull CustomEnum o) {
return ordinal - o.ordinal;
}
}
这个枚举基类很简单,有一个name和ordinal属性,可以和另一个CustomEnum进行compare.
现在,基于这个基类,定义两个枚举类
public class Color extends CustomEnum {
protected Color(String name, int ordinal) {
super(name, ordinal);
}
}
public class WeekDay extends CustomEnum {
protected WeekDay(String name, int ordinal) {
super(name, ordinal);
}
}
这两个也很简单。好,现在我们再定义几个具体的枚举实例
Color red = new Color("red", 0);
Color green = new Color("green", 1);
WeekDay Monday = new WeekDay("monday", 2);
也没问题。
现在,让我们来对它们进行compare
red.compareTo(green); // 正常
red.compareTo(Monday); // 也可以比较
对于第一个,red和green进行比较没问题,但,对于第二个,没有报错,竟然也可以比较,但这不合理啊!!!
那么,为什么red和Monday也可以比较呢?
这是因为Color中的compareTo方法是继承过来的,也就是
@Override
public int compareTo(@NotNull CustomEnum o) {
return ordinal - o.ordinal;
}
而至于这个方法能不能调用,只要调用的时候符合方法签名就行,这里Monday也是一个CustomEnum实例,那么,当然可以。
那么,我们怎么限定Color只能和Color对比,WeekDay只能和WeekDay对比呢?
其实我们就是想让
Color类中的compareTo是这样子的。
@Override
public int compareTo(@NotNull Color o) {
return ordinal - o.ordinal;
}
而WeekDay中的就是这样子的
@Override
public int compareTo(@NotNull WeekDay o) {
return ordinal - o.ordinal;
}
其实,如果Color和WeekDay直接实现Comparable<E>就行,就像下面这样
public class Color implements Comparable<Color> {
int value;
public Color(int value) {
this.value = value;
}
@Override
public int compareTo(@NotNull Color o) {
return 0;
}
}
public class WeekDay implements Comparable<WeekDay> {
int value;
public WeekDay(int value) {
this.value = value;
}
@Override
public int compareTo(@NotNull WeekDay o) {
return 0;
}
}
那为什么继承CustomEnum就不行了呢?
这是因为CustomEnum在实现Comparable<E>是,把类型参数给固定了。其它类再去继承CustomEnum时,就相当于继承一个普通的无类型参数的类了。
要想让Comparable的类型参数起作用,就不能在继承体系的非叶子结点(想象成一个树,最终的类就是叶子结点)将类型参数给固定了。所以,这些非叶子结点直接透传参数就行。如下面这样
public abstract static class CustomEnum<E> implements Comparable<E> {
private final String name;
private final int ordinal;
protected CustomEnum(String name, int ordinal) {
this.name = name;
this.ordinal = ordinal;
}
public final String name() {
return name;
}
public final int ordinal() {
return ordinal;
}
}
public class Color extends CustomEnum<Color> {
public Color(String name, int ordinal) {
super(name, ordinal);
}
@Override
public int compareTo(@NotNull Color o) {
return ordinal - o.ordinal;
}
}
public class WeekDay extends CustomEnum<WeekDay> {
public WeekDay(String name, int ordinal) {
super(name, ordinal);
}
@Override
public int compareTo(@NotNull WeekDay o) {
return ordinal - o.ordinal;
}
}
但现在,我们发现,无论是WeekDay还是Color,其compareTo方法几乎一样,就是方法参数一个是Color一个是WeekDay,那么,可不可以将这个逻辑移动到基类呢?
试一试
public abstract class CustomEnum<E> implements Comparable<E> {
protected final String name;
protected final int ordinal;
protected CustomEnum(String name, int ordinal) {
this.name = name;
this.ordinal = ordinal;
}
public final String name() {
return name;
}
public final int ordinal() {
return ordinal;
}
@Override
public int compareTo(@NotNull E o) {
return ordinal - o.ordinal; // 报错
}
}
不行啊,报错!
其实,也很好理解,在CustomEnum中,E就是一种任意类型,所以,找不到ordinal很正常不过了。
那么,怎么办呢?怎么告诉编译器 E有ordinal成员呢?我们可以强制如下这样
public abstract class CustomEnum<E> implements Comparable<E> {
protected final String name;
protected final int ordinal;
protected CustomEnum(String name, int ordinal) {
this.name = name;
this.ordinal = ordinal;
}
public final String name() {
return name;
}
public final int ordinal() {
return ordinal;
}
@Override
public int compareTo(@NotNull CustomEnum o) {
return ordinal - o.ordinal; // 报错
}
}
其实这样也行,只是,Color和Monday中的compareTo又回到最初那个了,因为java的类型擦除机制,所以Color和Monday其实都是CustomEnum类型(CustomeEnum<Color>和CustomEnum<WeekDay>中的类型都擦除了)
我们的目的依然没有达到。
另一种方式,为类型参数设定一个上界,如下
public abstract static class CustomEnum<E extends CustomEnum> implements Comparable<E> {
protected final String name;
protected final int ordinal;
protected CustomEnum(String name, int ordinal) {
this.name = name;
this.ordinal = ordinal;
}
public final String name() {
return name;
}
public final int ordinal() {
return ordinal;
}
@Override
public int compareTo(@NotNull E o) {
return ordinal - o.ordinal;
}
}
这个其实和上一个是一样的,因为这里只说明了E是extend了CustomEnum。而WeekDay也是extend了CustomEnum,所以,Color和WeekDay依然可以混用。
那么,这里的E必须就不能仅仅是extend了CustomEnum,还必须得是CustomeEnum,比如Color,这里就得是CustomEnum<Color>, 比如WeekDay, 这里就得是CustomEnum<WeekDay>
所以,E得是CustomEnum<E>
public abstract static class CustomEnum<E extends CustomEnum<E>> implements Comparable<E> {
protected final String name;
protected final int ordinal;
protected CustomEnum(String name, int ordinal) {
this.name = name;
this.ordinal = ordinal;
}
public final String name() {
return name;
}
public final int ordinal() {
return ordinal;
}
@Override
public int compareTo(@NotNull E o) {
return ordinal - o.ordinal();
}
}
总结
遇到这种复杂的不要怕,其实作者也不是一蹴而就,一步写成的。
慢慢简化,然后再一点点加回去,加的时候想想为啥要这样。
同时,也写写例子。
其实就是再现作者当初写的时候的思路。
这样就可以明白啦!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。