近日,在看Enum的定義時,看到其聲明爲java
public abstract class Enum<E extends Enum<E>> implements Comparable<E>
暫時忽略Constable和Serializable
那麼,爲何會這樣聲明呢?ide
先拋棄java中的定義,我們本身先定義一個Enum,看看在自定義過程當中會發生什麼。this
初版code
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);
也沒問題。table
如今,讓咱們來對它們進行compareclass
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(); } }
總結
遇到這種複雜的不要怕,其實做者也不是一蹴而就,一步寫成的。
慢慢簡化,而後再一點點加回去,加的時候想一想爲啥要這樣。
同時,也寫寫例子。
其實就是再現做者當初寫的時候的思路。
這樣就能夠明白啦!