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

2019-10-13 10:01:08 +08:00
 amiwrong123

最近看书刚看懂 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;这里三目表达式不是肯定判断为真吗

4136 次点击
所在节点    Java
28 条回复
xuanyu66
2019-10-13 15:20:54 +08:00
我其实也不是很懂为啥把”匿名内部类“规定为非静态内部类。静态内部类就不能匿名了吗
```
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"));
}
}
```
用这个对比更方便
amiwrong123
2019-10-13 15:36:03 +08:00
@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 的了。所以此时,匿名内部类不能持有外部类的引用。
xuanyu66
2019-10-13 16:01:42 +08:00
@amiwrong123 明白就 ok,我也是先跑测试了解了一下,共同学习了
xuanyu66
2019-10-13 16:02:33 +08:00
@amiwrong123 是在学 java 嘛,以后随时有问题都可以交流交流
amiwrong123
2019-10-13 16:11:13 +08:00
@xuanyu66
是呀,正在学呢。主要是看 java 编程思想这本书,不过看得仔细就读得慢了。关注你一波,以后好再 @你,哈哈哈。
xuanyu66
2019-10-13 16:18:39 +08:00
@amiwrong123 之前囫囵吞枣地看过,估计以后要重读这本书
amiwrong123
2019-10-13 16:32:54 +08:00
@xuanyu66
这本书挺好的,之前和它比还纠结 java 核心技术先看哪本,还是选了它。其实更重要的是,选了一本就好好看==
xuanyu66
2019-10-13 16:38:22 +08:00
@amiwrong123 核心技术我也看过了,那本书对入门者还不错的。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/608807

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX