values()的神秘之处

前面已经提到,编译器为你创建的enum类都继承自Enum类。然而,如果你研究一下Enum类就会发现,它并没有values()方法。可我们明明已经用过该方法了,难道存在某种“隐藏的”方法吗?我们可以利用反射机制编写一个简单的程序,来查看其中的究竟:

enum Explore {
    HERE, THERE
}
public class Reflection {
    public static Set<String> analyze(Class<?> enumClass) {
        System.out.println("--- Analyzing " + enumClass + "-----");
        System.out.println("Interface: ");
        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> analyze = analyze(Enum.class);

        System.out.println("Explore.containsAll(enumMethods)");
        System.out.println("---" + exploreMethods.containsAll(analyze));

        System.out.println("Explore.removeAll(Enum): ");
        exploreMethods.removeAll(analyze);
        System.out.println(exploreMethods);
    }
}
/*
--- Analyzing class com.jianglei.enum3.Explore-----
Interface: 
Base: class java.lang.Enum
Methods: 
[compareTo, equals, getClass, getDeclaringClass, hashCode, name, notify, notifyAll, ordinal, toString, valueOf, values, wait]
--- Analyzing class java.lang.Enum-----
Interface: 
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(enumMethods)
---true
Explore.removeAll(Enum): 
[values]
*/

答案是,values()是由编译器添加的static方法。可以看出,在创建Explore的过程中,编译器还为其添加了valueOf()方法。这可能有点令人迷惑,Enum类不是已经有valueOf()方法了吗。不过Enum中的valueOf()方法需要两个参数,而这个新增的方法只需要一个参数。由于这里使用的Set只存储方法的名字,而不考虑方法的签名,所以在调用Explore.removeAll(Enum)之后,就只剩下[values]了。 从最后的输出中可以看到,编译器将Explore标记为final类,所以无法继承自enum。其中还有一个static初始化子句。稍后我们将学习如何定义该句。 由于擦除效应(在第15章中介绍过),反编译无法得到Enum的完整信息,所以它展示的Explore的父类只是一个原始的Enum,而非事实上的Enum<Explore>。 由于values()方法是由编译器插入到enum定义中的static方法,所以,中果你将enum实例向上转型为Enum,那么values()方法就不可访问了。不过,在Class中有一个getEnumConstants()方法,所以即便Enum接口中没有values()方法,我们仍然可以通过Class对象取得所有enum实例:

public class UpcastEnum {
    public static void main(String[] args) {
        Search[] values = Search.values();
        Enum<Search> hither = Search.HITHER;
        Enum[] enumConstants = hither.getClass().getEnumConstants();
        for (Enum constant : enumConstants) {
            System.out.println(constant);
        }
    }
}
/*
HITHER
YON
*/

因为getEnumConstants()Class上的方法,所以你甚至可以对不是枚举的类调用此方法:

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(e);
        }
    }
}
/*
output:
java.lang.NullPointerException
*/

只不过,此时该方法返回null,所以当你试图使用其返回的结果时会发生异常。

results matching ""

    No results matching ""