Class#getEnumConstants() って初めて使った気がする

Enum#values()

ある Enum の値を取得したい場合は Enum の static メソッドである values() を使う。

public enum Fruits { APPLE, ORANGE, BANANA; }

Fruits.values();

この values() の実装は、コンパイラが生成してクラスに埋め込む。

言語仕様的には 8.9.2. Enum Body Declarations に以下のようにある


In addition, if E is the name of an enum type, then that type has the following implicitly declared static methods:

/**
* Returns an array containing the constants of this enum 
* type, in the order they're declared.  This method may be
* used to iterate over the constants as follows:
*
*    for(E c : E.values())
*        System.out.println(c);
*
* @return an array containing the constants of this enum 
* type, in the order they're declared
*/
public static E[] values();

/**
* Returns the enum constant of this type with the specified
* name.
* The string must match exactly an identifier used to declare
* an enum constant in this type.  (Extraneous whitespace 
* characters are not permitted.)
* 
* @return the enum constant with the specified name
* @throws IllegalArgumentException if this enum type has no
* constant with the specified name
*/
public static E valueOf(String name);

例えば

以下のようなインターフェースがあって、

public interface Named {
    String getName();
}

enum で実装して、

public enum Fruits implements Named {

    APPLE("Apple"), ORANGE("Orange"), BANANA("Banana");

    private final String name;
    private Fruits(String name) { this.name = name; }
    public String getName() {
        return name;
    }
}

Named を実装した enum の要素を扱いたい場合、

public <T extends Enum<T> & Named> List<String> allFruits(Class<T> enumClass) {
    return Stream.of(enumClass.values())
            .map(e -> e.name() + "(" + e.getName() + ")")
            .collect(Collectors.toList());
}

としても、enumClass.values() はできない。

Class#getEnumConstants()

Class にある getEnumConstants() を使えば良い。

public <T extends Enum<T> & Named> List<String> allFruits(Class<T> enumClass) {
    return Stream.of(enumClass.getEnumConstants())
            .map(e -> e.name() + "(" + e.getName() + ")")
            .collect(Collectors.toList());
}

以下のように取れる。

allFruits(Fruits.class).forEach(System.out::println);

APPLE(Apple)
ORANGE(Orange)
BANANA(Banana)


getEnumConstants() の実装はこんなふうになってた。

    /**
     * Returns the elements of this enum class or null if this
     * Class object does not represent an enum type.
     *
     * @return an array containing the values comprising the enum class
     *     represented by this Class object in the order they're
     *     declared, or null if this Class object does not
     *     represent an enum type
     * @since 1.5
     */
    public T[] getEnumConstants() {
        T[] values = getEnumConstantsShared();
        return (values != null) ? values.clone() : null;
    }

    /**
     * Returns the elements of this enum class or null if this
     * Class object does not represent an enum type;
     * identical to getEnumConstants except that the result is
     * uncloned, cached, and shared by all callers.
     */
    T[] getEnumConstantsShared() {
        if (enumConstants == null) {
            if (!isEnum()) return null;
            try {
                final Method values = getMethod("values");
                java.security.AccessController.doPrivileged(
                    new java.security.PrivilegedAction<Void>() {
                        public Void run() {
                                values.setAccessible(true);
                                return null;
                            }
                        });
                @SuppressWarnings("unchecked")
                T[] temporaryConstants = (T[])values.invoke(null);
                enumConstants = temporaryConstants;
            }
            // These can happen when users concoct enum-like classes
            // that don't comply with the enum spec.
            catch (InvocationTargetException | NoSuchMethodException |
                   IllegalAccessException ex) { return null; }
        }
        return enumConstants;
    }
    private volatile transient T[] enumConstants = null;

ふむ。リフレクションで values メソッド呼ぶのね。。