Java Enum 类values 的神秘作用

所有的枚举类型都是由编译器通过继承 Enum 类来创建的。然而,如果仔细查看 Enum 类的代码,你会发现里面并没有 values() 方法,我们却已经能直接使用它了。是哪里有其他“隐藏”的方法吗?我们可以编写一个小的反射程序来一探究竟:

// enums/Reflection.java
// 使用反射分析枚举类
import java.lang.reflect.*;
import java.util.*;
import onjava.*;

enum Explore { HERE, THERE }

public class Reflection {
  public static
  Set<String> analyze(Class<?> enumClass) {
    System.out.println(
      "_____ Analyzing " + enumClass + " _____");
    System.out.println("Interfaces:");
    for(Type t : enumClass.getGenericInterfaces())
      System.out.println(t);
    System.out.println(
      "Base: " + enumClass.getSuperclass());
    System.out.println("Methods: ");
    Set<String> methods = new TreeSet<>();
    for(Method m : enumClass.getMethods())
      methods.add(m.getName());
    System.out.println(methods);
    return methods;
  }
  public static void main(String[] args) {
    Set<String> exploreMethods =
      analyze(Explore.class);
    Set<String> enumMethods = analyze(Enum.class);
    System.out.println(
      "Explore.containsAll(Enum)? " +
      exploreMethods.containsAll(enumMethods));
    System.out.print("Explore.removeAll(Enum): ");
    exploreMethods.removeAll(enumMethods);
    System.out.println(exploreMethods);
    // 反编译enum:
    OSExecute.command(
      "javap -cp build/classes/java/main Explore");
  }
}
/* 输出:
_____ Analyzing class Explore _____
Interfaces:
Base: class java.lang.Enum
Methods:
[compareTo, equals, getClass, getDeclaringClass,
hashCode, name, notify, notifyAll, ordinal, toString,
valueOf, values, wait]
_____ Analyzing class java.lang.Enum _____
Interfaces:
java.lang.Comparable<E>
interface java.io.Serializable
Base: class java.lang.Object
Methods:
[compareTo, equals, getClass, getDeclaringClass,
hashCode, name, notify, notifyAll, ordinal, toString,
valueOf, wait]
Explore.containsAll(Enum)? true
Explore.removeAll(Enum): [values]
Compiled from "Reflection.java"
final class Explore extends java.lang.Enum<Explore> {
  public static final Explore HERE;
  public static final Explore THERE;
  public static Explore[] values();
  public static Explore valueOf(java.lang.String);
  static {};
}
*/

答案揭晓,values() 方法是由编译器添加的一个静态方法。注意在创建枚举的过程中,valueOf() 方法同样也被添加到了 Explore 枚举中。这有点让人糊涂,Enum 类中同样也有一个 valueOf()方法,但是该方法有 2 个参数,而新加入的方法则只有 1 个。然而,这里的 Set 方法只关心方法名,并不关心方法签名,所以在调用 Explore.removeAll(Enum) 后,只剩下了 [values]。

打印结果显示 Explore 枚举被编译器限定为 final 类,所以你无法继承一个枚举类。此外还有一个 static 的初始化子句,你稍后会看到它可以被重定义。

由于类型擦除(相关介绍参见基础卷第 20 章)的缘故,反编译器得不到 Enum 类的完整信息,因此只能将 Explore 类的基类作为一个原始的 Enum 类来显示,而不是实际上的 Enum。

由于 values() 方法是由编译器在枚举类的定义中插入的一个静态方法,因此如果你将枚举类型向上转型为 Enum,则values() 方法将不可用。然而要注意的是,Class 中有个 getEnumConstants() 方法,所以即使 Enum 的接口中没有 values() 方法,仍然可以通过 Class 对象来得到 enum 的实例:

// enums/UpcastEnum.java
// 如果向上转型枚举,便会丢失values()方法

enum Search { HITHER, YON }

public class UpcastEnum {
  public static void main(String[] args) {
    Search[] vals = Search.values();
    Enum e = Search.HITHER; // 向上转型
    // e.values(); // Enum中没有values()方法
    for(Enum en : e.getClass().getEnumConstants())
      System.out.println(en);
  }
}
/* 输出:
HITHER
YON
*/
由于 getEnumConstants()Class 类中的一个方法,因此对一个没有枚举的类也可以调用该方法。


// enums/NonEnum.java

public class NonEnum {
  public static void main(String[] args) {
    Class<Integer> intClass = Integer.class;
    try {
      for(Object en : intClass.getEnumConstants())
        System.out.println(en);
    } catch(Exception e) {
      System.out.println("Expected: " + e);
    }
  }
}
/* 输出:
Expected: java.lang.NullPointerException
*/

该方法会返回 null,因此如果你尝试引用该结果,就会抛出异常。

作者:图灵教育
链接:https://leetcode.cn/leetbook/read/on-java-zhong-wen-ban-jin-jie-juan/laam83/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值