近日,在看 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();
}
}
总结
遇到这种简单的不要怕,其实作者也不是欲速不达,一步写成的。
缓缓简化,而后再一点点加回去,加的时候想想为啥要这样。
同时,也写写例子。
其实就是再现作者当初写的时候的思路。
这样就能够明确啦!