关于java:解读-EnumE-extends-EnumE

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

总结

遇到这种简单的不要怕,其实作者也不是欲速不达,一步写成的。
缓缓简化,而后再一点点加回去,加的时候想想为啥要这样。

同时,也写写例子。

其实就是再现作者当初写的时候的思路。

这样就能够明确啦!

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理