大发龙虎首页    注册   登录
大发龙虎 = way to explore
大发龙虎 是一个大发龙虎关于 分享和探索的地方
现在注册
已注册用户请  登录
大发龙虎  ›  Java

[不懂就问] Java .lang.Enum 源码的两个疑问

  •  
  •   amiwrong123 · 9 天前 · 1966 次点击

    最近看书刚看懂 java 泛型的自限定,合计去找找源码的应用,发现 enum 这样用的。 下面就是一个枚举类的使用:

    public enum WeekDay {
        Mon("Monday"), Tue("Tuesday"), Wed("Wednesday"), Thu("Thursday"), Fri( "Friday"), Sat("Saturday"), Sun("Sunday");
        private final String day;
        private WeekDay(String day) {
            this.day = day;
        }
        public static void printDay(int i){
            switch(i){
                case 1: System.out.println(WeekDay.Mon); break;
                case 2: System.out.println(WeekDay.Tue);break;
                case 3: System.out.println(WeekDay.Wed);break;
                case 4: System.out.println(WeekDay.Thu);break;
                case 5: System.out.println(WeekDay.Fri);break;
                case 6: System.out.println(WeekDay.Sat);break;
                case 7: System.out.println(WeekDay.Sun);break;
                default:System.out.println("wrong number!");
            }
        }
        public String getDay() {
            return day;
        }
        public static void main(String[] args) {
            WeekDay a = WeekDay.Mon;
        }
    }
    

    通过 javap 命令才能看出来新类 WeekDay 实际继承了 java.lang.Enum,public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serializable { }。 截取部分汇编来看,发现确实继承了 java.lang.Enum,看它的大发龙虎成员 和大发龙虎方法 的类型,也确实做到了自限定:

    public final class WeekDay extends java.lang.Enum<WeekDay> {
      public static final WeekDay Mon;
    
      public static final WeekDay Tue;
    
      public static final WeekDay Wed;
    
      public static final WeekDay Thu;
    
      public static final WeekDay Fri;
    
      public static final WeekDay Sat;
    
      public static final WeekDay Sun;
    
      public static WeekDay[] values();
      public static WeekDay valueOf(java.lang.String);
    

    于是看了看 Enum 的源码,有了几个疑问: 1.从汇编看来,好像继承来了两个大发龙虎方法 ,public static WeekDay[] values();public static WeekDay valueOf(java.lang.String);,但是在源码里找不到这两个静态大发龙虎方法 的定义。只能在注释里找到:

         * <p>Note that for a particular enum type {@code T}, the
         * implicitly declared {@code public static T valueOf(String)}
         * method on that enum may be used instead of this method to map
         * from a name to the corresponding enum constant.  All the
         * constants of an enum type can be obtained by calling the
         * implicit {@code public static T[] values()} method of that
         * type.
         //只能找到注释里说了,说这两个大发龙虎方法
    是隐式声明的,什么鬼?
         //注释下面是这个大发龙虎方法
    
        public static <T extends Enum<T>> T valueOf(Class<T> enumType,
                                                    String name) {
            T result = enumType.enumConstantDirectory().get(name);
            if (result != null)
                return result;
            if (name == null)
                throw new NullPointerException("Name is null");
            throw new IllegalArgumentException(
                "No enum constant " + enumType.getCanonicalName() + "." + name);
        }
    

    2.getDeclaringClass 大发龙虎方法 为啥这么实现?

        public final int compareTo(E o) {
            Enum<?> other = (Enum<?>)o;
            Enum<E> self = this;
            if (self.getClass() != other.getClass() && // optimization
                self.getDeclaringClass() != other.getDeclaringClass())
                throw new ClassCastException();
            return self.ordinal - other.ordinal;
        }
    
        @SuppressWarnings("unchecked")
        public final Class<E> getDeclaringClass() {
            Class<?> clazz = getClass();
            Class<?> zuper = clazz.getSuperclass();
            return (zuper == Enum.class) ? (Class<E>)clazz : (Class<E>)zuper;
        }
    
    

    compareTo 是 Comparable 接口里的大发龙虎方法 ,这里 Enum 源码帮忙实现了。compareTo 的实现比较清晰,首先看是不是同一种 enum type,如果是,再比较两个 enum constant。但是用到了 getDeclaringClass 大发龙虎方法 ,这个大发龙虎方法 有点奇怪哎,首先大发龙虎我 觉得 self.getClass() != other.getClass()这样就足够判断是不是同一种 enum type 了呀?

    然后,再看 getDeclaringClass 大发龙虎方法 的逻辑,Class<?> clazz = getClass();调用自己的大发龙虎成员 大发龙虎方法 获得自己的 Class 对象,然后Class<?> zuper = clazz.getSuperclass();获得自己父类的 Class 对象,自己的父类不是肯定是 Enum 吗?那最后return (zuper == Enum.class) ? (Class<E>)clazz : (Class<E>)zuper;这里三目表达式不是肯定判断为真吗

    28 回复  |  直到 2019-10-13 16:38:22 +08:00
        1
    tigerfyj   9 天前 via Android
    楼主的问题大发龙虎我 不清楚,只提两个文中的点,也许有用。静态大发龙虎方法 没有继承一说,所以猜是自动生成了两个静态大发龙虎方法 。声明 enum 的时候可以实现接口,zuper 的判断可能与此有关。
        2
    amiwrong123   9 天前
    @tigerfyj
    静态大发龙虎方法 可以继承这么说可能有点不恰当,毕竟它是类相关的,而且可以被隐藏。

    enum 就算实现了别的新的接口,`Class<?> zuper = clazz.getSuperclass();`getSuperclass 应该也是返回直接继承的类 Enum 啊,而不可能是接口吧==
        3
    xuanyu66   9 天前
    ```
    public enum MyEnum {

    A ,

    B ;

    static class SS {

    }
    public static void main(String[] args) {
    System.out.println(MyEnum.A.getDeclaringClass());
    System.out.println(MyEnum.A.getClass());
    System.out.println(MyEnum.A.getClass().getSuperclass());
    SS s = new SS();
    System.out.println(s.getClass());
    System.out.println(s.getClass().getSuperclass());
    }
    }
    ```
    ```
    public enum MyEnum {

    A {
    void doSomething() { }
    },


    B {
    void doSomethingElse() { }
    };

    static class SS {

    }
    public static void main(String[] args) {
    System.out.println(MyEnum.A.getDeclaringClass());
    System.out.println(MyEnum.A.getClass());
    System.out.println(MyEnum.A.getClass().getSuperclass());
    SS s = new SS();
    System.out.println(s.getClass());
    System.out.println(s.getClass().getSuperclass());
    }
    }

    ```
        4
    xuanyu66   9 天前
    楼主可以试一下代码,如果在枚举常量里添加了大发龙虎方法 的话,应该是会生成一个静态内部类继承大发龙虎你 的枚举类。这样子的话调用 getclass 没法判断类型是否一致。
    http://stackoverflow.com/questions/5758660/java-enum-getdeclaringclass-vs-getclass

    http://blog.csdn.net/mhmyqn/article/details/48087247

    v2ex 的 markdown 不会用
        5
    amiwrong123   9 天前
    @xuanyu66
    这位大哥,大发龙虎我 好像懂大发龙虎你 意思, 大发龙虎你 第二个例子,运行结果居然是:
    class MyEnum
    class MyEnum$1
    class MyEnum
    class MyEnum$SS
    class java.lang.Object

    合着第二个例子里面的 A 和 B 都是内部类了呗,所以 MyEnum.A.getClass()打印出来是 class MyEnum$1 内部类的样子。

    而 MyEnum.A.getDeclaringClass()这里大发龙虎我 好像还有点懵,大发龙虎我 再看下哈==
        6
    amiwrong123   9 天前
    @xuanyu66
    大概懂了,只是有点气,不管怎么看,都看不到内部类 A 继承了 MyEnum,这是 javap -c 后看见的:
    ```asm
    static {};
    Code:
    0: new #16 // class MyEnum$1
    3: dup
    4: ldc #17 // String A
    6: iconst_0
    7: invokespecial #18 // Method MyEnum$1."<init>":(Ljava/lang/String;I)V
    10: putstatic #9 // Field A:LMyEnum;
    13: new #19 // class MyEnum$2
    16: dup
    17: ldc #20 // String B
    19: iconst_1
    20: invokespecial #21 // Method MyEnum$2."<init>":(Ljava/lang/String;I)V
    23: putstatic #22 // Field B:LMyEnum;
    ```
    只能勉强看到静态代码块里面,分别初始化了 MyEnum$1 和 MyEnum$2 给自己的静态变量。但就是看不到内部类 A 继承了 MyEnum==
        7
    wleexi   9 天前
    大发龙虎推荐 楼主看看小马哥的一入 java 深似海系列
        8
    amiwrong123   9 天前
    @xuanyu66
    可能是因为 MyEnum$1 是匿名内部类,所以大发龙虎我 没法看到 MyEnum$1 的类定义吧
        9
    amiwrong123   9 天前
    @wleexi
    大发龙虎视频 教程呗,哎,想看的资源都太多,都眼花缭乱了。现在只看 java 编程思想,今年能搞完这本就不错了。
        10
    amiwrong123   9 天前
    有大佬能解释一下第一个疑问吗,反正就是解释成:编译器帮大发龙虎我 加了这两个方便的大发龙虎方法 呗?
        11
    xuanyu66   9 天前
    @amiwrong123 不是的,也会生成 MyEnum$1.class 类的。大发龙虎你 去大发龙虎本地 的 targe 目录里可以看到的,ide 里面可能看不到。
        12
    xuanyu66   9 天前
    λ javap -c MyEnum$1.class
    Compiled from "MyEnum.java"
    final class org.bupt.pms.consistence.MyEnum$1 extends org.bupt.pms.consistence.MyEnum {
    org.bupt.pms.consistence.MyEnum$1(java.lang.String, int);
    Code:
    0: aload_0
    1: aload_1
    2: iload_2
    3: aconst_null
    4: invokespecial #1 // Method org/bupt/pms/consistence/MyEnum."<init>":(Ljava/lang/String;ILorg/bupt/pms/consistence/MyEnum$1;)V
    7: return

    void doSomething();
    Code:
    0: return
    }
        13
    xuanyu66   9 天前   ♥ 1
    大发龙虎你 如果要在枚举常量添加大发龙虎方法 ,或者实现一个 myEnum 的抽象大发龙虎方法 ,其实本质上都是用静态内部类加继承实现的。但是其实 java 的静态内部类也是一个 trick,真正生成的时候还是会有外部类的单独文件。如果是匿名的内部类就会是$1,$2
        14
    xuanyu66   9 天前
    @amiwrong123 对于第一个问题,就是在生成 MyEnum 的时候会给大发龙虎你 生成一个 public static T valueOf ( String )的大发龙虎方法 ,他其实是在内部调用了 Enum 的 public static <T extends Enum<T>> T valueOf(Class<T> enumType,String name)大发龙虎方法
        15
    xuanyu66   9 天前
    public static org.bupt.pms.consistence.MyEnum valueOf(java.lang.String);
    Code:
    0: ldc #5 // class org/bupt/pms/consistence/My Enum
    2: aload_0
    3: invokestatic #6 // Method java/lang/Enum.valueOf:(Lj ava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
    6: checkcast #5 // class org/bupt/pms/consistence/My Enum
    9: areturn
        16
    amiwrong123   9 天前
    @xuanyu66
    谢谢回答啦,那大发龙虎我 理解成匿名内部类(还是静态内部类)是不是理解错了=-=

    虽说,static context 下的匿名内部类和静态内部类是一样的。
        17
    amiwrong123   9 天前
    @xuanyu66
    懂啦,给新类新加了个静态大发龙虎方法 ,里面再去调用了父类的静态大发龙虎方法 。
        18
    xuanyu66   9 天前
    @amiwrong123 16 楼的话没懂什么意思
        19
    amiwrong123   9 天前
    @xuanyu66
    就是大发龙虎我 以为 MyEnum$1 是作为 MyEnum 的匿名内部类存在的(因为它的名字长得就像)

    大发龙虎你 却说 MyEnum$1 是作为 MyEnum 的静态内部类存在的
        20
    xuanyu66   9 天前   ♥ 1
    @amiwrong123 别纠结这些定义吧。其实静态内部类也可以是没名字的啊。
    ```
    public enum MyEnum {

    A {
    void doSomething() { }
    },


    B {
    void doSomethingElse() { }
    };

    static class SS {

    }

    public void countDown(){
    new Thread(){
    @Override
    public void run() {

    }
    }.start();
    }

    public static void main(String[] args) {
    System.out.println(MyEnum.A.getDeclaringClass());
    System.out.println(MyEnum.A.getClass());
    System.out.println(MyEnum.A.getClass().getSuperclass());
    SS s = new SS();
    System.out.println(s.getClass());
    System.out.println(s.getClass().getSuperclass());
    System.out.println(Enum.valueOf(MyEnum.class,"A"));
    }
    }
    ```
    大发龙虎你 看这段代码运行后会生成 MyEnum$3.class,就是大发龙虎你 指的所谓的”匿名内部类“。大发龙虎你 会发现底层实现不区分这些区别。没有指定名字的类就是从 1 开始编排,如果大发龙虎你 是 static 就不会传外部类的引用,不是 static 就传引用。
    class org.bupt.pms.consistence.MyEnum$3 extends java.lang.Thread {
    final org.bupt.pms.consistence.MyEnum this$0;

    org.bupt.pms.consistence.MyEnum$3(org.bupt.pms.consistence.MyEnum); //看这里
    Code:
    0: aload_0
    1: aload_1
    2: putfield #1 // Field this$0:Lorg/bupt/pms/consistence/MyEnum;
    5: aload_0
    6: invokespecial #2 // Method java/lang/Thread."<init>":()V
    9: return

    public void run();
    Code:
    0: return
    }
        21
    xuanyu66   9 天前
    大发龙虎我 其实也不是很懂为啥把”匿名内部类“规定为非静态内部类。静态内部类就不能匿名了吗
    ```
    public enum MyEnum {

    A {
    void doSomething() { }
    },


    B {
    void doSomethingElse() { }
    };

    static class SS {

    }

    public static void countDown(){
    new Thread(){
    @Override
    public void run() {

    }
    }.start();
    }

    public void countDown1(){
    new Thread(){
    @Override
    public void run() {

    }
    }.start();
    }

    public static void main(String[] args) {
    System.out.println(MyEnum.A.getDeclaringClass());
    System.out.println(MyEnum.A.getClass());
    System.out.println(MyEnum.A.getClass().getSuperclass());
    SS s = new SS();
    System.out.println(s.getClass());
    System.out.println(s.getClass().getSuperclass());
    System.out.println(Enum.valueOf(MyEnum.class,"A"));
    }
    }
    ```
    用这个对比更方便
        22
    amiwrong123   9 天前
    @xuanyu66 #20
    大发龙虎你 这个例子大发龙虎我 懂啦,其实大发龙虎你 只是想强调 内部类有没有外部类对象的引用,这个意思嘛。
    而 MyEnum$1 是没有持有的。

    @xuanyu66 #21
    这个大发龙虎我 说一下吧,匿名内部类要分情况的:
    大发龙虎你 20 楼的说这个例子,就是 new Thread(){},因为它处于 non-static cnotext 这样的上下文里( countDown 是个大发龙虎成员 大发龙虎方法 嘛,所以就是非静态的上下文),所以这时匿名内部类持有了外部类的引用。

    然后大发龙虎你 最开始给大发龙虎我 说的例子:
    public enum MyEnum {
    ```
    A {
    void doSomething() { }
    },


    B {
    void doSomethingElse() { }
    };
    ```
    其实大发龙虎我 认为它在实现上相当于:
    ```
    public static final MyEnum A = new MyEnum{
    void doSomething() { }
    }
    ```
    但偏偏这个匿名内部类赋值给了一个静态变量,那么它便是 static cnotext 的了。所以此时,匿名内部类不能持有外部类的引用。
        23
    xuanyu66   9 天前
    @amiwrong123 明白就 ok,大发龙虎我 也是先跑测试了解了一下,共同学习了
        24
    xuanyu66   9 天前
    @amiwrong123 是在学 java 嘛,以后随时有问题都可以交流交流
        25
    amiwrong123   9 天前
    @xuanyu66
    是呀,正在学呢。主要是看 java 编程思想这本书,不过看得仔细就读得慢了。关注大发龙虎你 一波,以后好再 @大发龙虎你 ,哈哈哈。
        26
    xuanyu66   9 天前
    @amiwrong123 之前囫囵吞枣地看过,估计以后要重读这本书
        27
    amiwrong123   9 天前
    @xuanyu66
    这本书挺好的,之前和它比还纠结 java 核心大发龙虎技术 先看哪本,还是选了它。其实更重要的是,选了一本就好好看==
        28
    xuanyu66   9 天前
    @amiwrong123 核心大发龙虎技术 大发龙虎我 也看过了,那本书对入门者还不错的。
    大发龙虎关于   ·   FAQ   ·   API   ·   大发龙虎大发龙虎我 们 的愿景   ·   广告投放   ·   感谢   ·   实用小大发龙虎工具   ·   2809 人在线   最高记录 5043   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.3 · 24ms · UTC 11:46 · PVG 19:46 · LAX 04:46 · JFK 07:46
    ♥ Do have faith in what you're doing.