解讀 Enum >

近日,在看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();
        }
    }

總結

遇到這種複雜的不要怕,其實做者也不是一蹴而就,一步寫成的。
慢慢簡化,而後再一點點加回去,加的時候想一想爲啥要這樣。

同時,也寫寫例子。

其實就是再現做者當初寫的時候的思路。

這樣就能夠明白啦!

相關文章
相關標籤/搜索