[Java] 枚举常量的精确类型一定是当前枚举类型吗?

java 枚举常量的精确类型一定是当前枚举类型吗?

枚举常量的精确类型是当前枚举类型的例子

有的朋友可能会说,枚举常量的精确类型当然是当前枚举类型。随便写段代码就能验证 ⬇️

java 复制代码
public enum Direction {
    EAST, WEST, SOUTH, NORTH;

    public static void main(String[] args) {
        for (var direction : Direction.values()) {
            System.out.println(direction.getClass().getName());
        }
    }
}

请将上面的代码保存为 Direction.java。用如下命令可以编译 Direction.java 并运行其中的 main(...) 方法。

bash 复制代码
javac Direction.java && java Direction

运行结果为 ⬇️

text 复制代码
Direction
Direction
Direction
Direction

可见 EAST, WEST, SOUTH, NORTH 4 4 4 个枚举常量的精确类型都是 Direction。 运行 javap -p Direction 命令可以看到 Direction.class 文件的简要内容 ⬇️

java 复制代码
Compiled from "Direction.java"
public final class Direction extends java.lang.Enum<Direction> {
  public static final Direction EAST;
  public static final Direction WEST;
  public static final Direction SOUTH;
  public static final Direction NORTH;
  private static final Direction[] $VALUES;
  public static Direction[] values();
  public static Direction valueOf(java.lang.String);
  private Direction();
  public static void main(java.lang.String[]);
  private static Direction[] $values();
  static {};
}

从中可以看出,EAST, WEST, SOUTH, NORTHDirection 类的 4 4 4 个实例,所以这些枚举常量的精确类型是当前枚举类型(即 Direction 类)。

但是,这样并不能说明没有反例存在。

枚举常量的精确类型不是当前枚举类型的例子

Java Language Specification 中的 8.9.3. Enum Members 小节 里有这样的内容 ⬇️

我把这里的 Operation 枚举类做了些调整 ⬇️

java 复制代码
public enum Operation {
    PLUS {
        double eval(double x, double y) { return x + y; }
    },
    MINUS {
        double eval(double x, double y) { return x - y; }
    },
    TIMES {
        double eval(double x, double y) { return x * y; }
    },
    DIVIDED_BY {
        double eval(double x, double y) { return x / y; }
    };

    // Each constant supports an arithmetic operation
    abstract double eval(double x, double y);

    public static void main(String[] args) {
        System.out.println("The name of Operation.class is: " + Operation.class.getName());
        System.out.println();
        for (var operation : Operation.values()) {
            System.out.println("The name of " + operation.name() + ".getClass() is: " + operation.getClass().getName());
        }
    }
}

请将上方的代码保存为 Operation.java。 用如下命令可以编译 Operation.java 并运行其中的 main(...) 方法。

bash 复制代码
javac Operation.java && java Operation 

运行结果如下 ⬇️

text 复制代码
The name of Operation.class is: Operation

The name of PLUS.getClass() is: Operation$1
The name of MINUS.getClass() is: Operation$2
The name of TIMES.getClass() is: Operation$3
The name of DIVIDED_BY.getClass() is: Operation$4

在这个例子中,枚举常量的精确类型分别是 Operation$1/Operation$2/Operation$3/Operation$4。这都是什么?别急,我们来看看有哪些 class 文件。在编译之后,当前目录下出现了 5 5 5 个 class 文件 ⬇️

  • Operation.class
  • Operation$1.class
  • Operation$2.class
  • Operation$3.class
  • Operation$4.class

如果我们执行 javap -v -p Operation 命令,会看到 Operation.class 的具体内容。但是字节码的内容读起来有点费劲,而且也不是本文的重点,所以我们借助 Intellij IDEA 来看看 Operation.class 反编译后的结果吧 ⬇️ (这里展示的结果并不完整,但截图之外的部分本文用不到)

从反编译的结果可以看出,PLUS, MINUS, TIMES, DIVIDED_BY 分别是 Operation 的某个匿名内部类的实例。

枚举常量 精确类型 说明
Operation.PLUS Operation$1 Operation$1Operation 的一个匿名内部类
Operation.MINUS Operation$2 Operation$2Operation 的一个匿名内部类
Operation.TIMES Operation$3 Operation$3Operation 的一个匿名内部类
Operation.DIVIDED_BY Operation$4 Operation$4Operation 的一个匿名内部类

对应的类图如下 ⬇️ (有些字段/方法省略了)

classDiagram `java.lang.Enum` <|-- Operation class Operation <<Abstract>> `java.lang.Enum` <<Abstract>> Operation Operation <|-- `Operation$1` Operation <|-- `Operation$2` Operation <|-- `Operation$3` Operation <|-- `Operation$4` Operation: abstract double eval(double, double) `Operation$1`: double eval(double, double) `Operation$2`: double eval(double, double) `Operation$3`: double eval(double, double) `Operation$4`: double eval(double, double)

相关的方法

是否有方法可以同时支持以下两种情况呢?⬇️

  • 通过 Direction.EAST 得到 Direction
  • 通过 Operation.PLUS 得到 Operation

Enum.java 里可以找到这个方法 ⬇️

java 复制代码
    /**
     * Returns the Class object corresponding to this enum constant's
     * enum type.  Two enum constants e1 and  e2 are of the
     * same enum type if and only if
     *   e1.getDeclaringClass() == e2.getDeclaringClass().
     * (The value returned by this method may differ from the one returned
     * by the {@link Object#getClass} method for enum constants with
     * constant-specific class bodies.)
     *
     * @return the Class object corresponding to this enum constant's
     *     enum type
     */
    @SuppressWarnings("unchecked")
    public final Class<E> getDeclaringClass() {
        Class<?> clazz = getClass();
        Class<?> zuper = clazz.getSuperclass();
        return (zuper == Enum.class) ? (Class<E>)clazz : (Class<E>)zuper;
    }

从代码中可以看出,这个方法可以覆盖本文提到的两种情况。这个方法返回的值一定是当前枚举类型。

参考资料

相关推荐
chen<>11 分钟前
C++ 六种 memory_order:从原子性到线程间可见性.md
java·linux·开发语言·c++
十年Java程序媛14 分钟前
SpringBoot3 + JDK17 实战复盘:HikariCP 连接耗尽,虚拟线程场景额外注意事项
java·spring boot
二十雨辰1 小时前
[Java]-JVM面试题
java·开发语言·jvm
cfm_29141 小时前
ConcurrentHashMap 线程安全机制与 JDK 1.8 演进
java·开发语言·安全
tqs_123451 小时前
值传递与引用传递
java·开发语言·python
2601_962181961 小时前
Spring boot从0到1 - day01
java·spring boot·后端
典典分享指南1 小时前
短视频分镜提示词:让 AI 出片的产品口播
java·c#·bash·composer·symfony
IT_Octopus1 小时前
G1 与 GC 日志入门课程(以本次离线菜单 Full GC 事故为教材)
java·jvm
cfm_29141 小时前
ReentrantLock 中 lock 与 tryLock 核心区别
java
.Hypocritical.2 小时前
Tomcat本地部署+远程服务器部署超详细教程
java·服务器·tomcat