JDK-6436941 : (enum) add method getEnumConstant(int i) to java.lang.Class
  • Type: Enhancement
  • Component: core-libs
  • Sub-Component: java.lang:reflect
  • Affected Version: 6
  • Priority: P4
  • Status: Closed
  • Resolution: Won't Fix
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2006-06-09
  • Updated: 2015-01-28
  • Resolved: 2015-01-28
Related Reports
Duplicate :  
Relates :  
Relates :  
Description
A DESCRIPTION OF THE REQUEST :
Currently, the method "public T[] getEnumConstants()" in java.lang.Class returns a clone of the values array of an enumeration class.  Presumably it does so to prevent the caller from altering the enumeration values.  However, this is quite inefficient if the caller is merely trying to obtain the Nth enumeration constant from the values array.

Adding another method "public T getEnumConstant(int N)" would return the Nth enumerated constant without requiring the values array to be cloned (since no reference to the array would escape the method).  (Returning null if N < 0 or N >= values.length, or if the class is not an enumeration, seems appropriate.)

JUSTIFICATION :
I'm writing a method to obtain the Nth enumeration value of an arbitrary enumeration.  I'm not able to simply use the values() method implicitly available for every enumeration because the particular enumeration is not available at compile time.  Instead, I'm given a Class<T> where <T extends Enum<T>>, and an integer value, i.e.

public static  <T extends Enum<T>> T verifyEnum(Class<T> type, int value)
{
    return type.getEnumConstants()[value]; // error checking omitted
}

However, this results in a clone operation of the values array every time it is called.  (And the HotSpot JIT isn't smart enough to see that the only usage of the cloned array is read-only, and therefore the original array could be used, avoiding the clone operation.)

Implementing a "getEnumConstant" method provides a simple way to avoid the cloning operation.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
I would like to simply write:

public static  <T extends Enum<T>> T verifyEnum(Class<T> type, int value)
{
    return type.getEnumConstant(value);
}

which would call the following new method in java.lang.Class:

   public T[] getEnumConstant(int N) {
	if (enumConstants == null) {
	    if (!isEnum()) return null;
	    try {
		final Method values = getMethod("values");
                java.security.AccessController.doPrivileged
                    (new java.security.PrivilegedAction() {
                            public Object run() {
                                values.setAccessible(true);
                                return null;
                            }
                        });
		enumConstants = (T[])values.invoke(null);
	    }
	    // These can happen when users concoct enum-like classes
	    // that don't comply with the enum spec.
	    catch (InvocationTargetException ex) { return null; }
	    catch (NoSuchMethodException ex) { return null; }
	    catch (IllegalAccessException ex) { return null; }
	}
                    if (N < 0 || N >= enumConstants.length) return null;
                    else
	return enumConstants[N];
    }

Obviously you would refactor the common code which obtains enumConstants the first time.

---------- BEGIN SOURCE ----------
public static synchronized <T extends Enum<T>> T verifyEnum(Class<T> type,
                                                                int      value)
{
    return type.getEnumConstants()[value];
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
I cache all enum constant arrays being returned in a HashMap so that subsequent calls don't have the cloning overhead, as follows:

protected static final Map<Class<? extends Enum>, List> enumConstantMap;

static {
        enumConstantMap = new WeakHashMap<Class<? extends Enum>, List>();
}


public static synchronized <T extends Enum<T>> T verifyEnum(Class<T> type,
                                                                int      value)
    {
        List enumConstants = enumConstantMap.get(type);
        if (enumConstants == null) {
            enumConstants = new ArrayList<T>(Arrays.asList(type.getEnumConstants()));
            enumConstantMap.put(type, enumConstants);
        }
        if (value < 0 || value >= enumConstants.size())
        {
            return null;
        }
        return type.cast(enumConstants.get(value));
    }

Comments
The need for this specific functionality seems limited and caching the returned array seems adequate given the expected frequency of this use case. Closing as will not fix.
28-01-2015

EVALUATION Contribution forum : https://jdk-collaboration.dev.java.net/servlets/ProjectForumMessageView?forumID=1463&messageID=18798
26-02-2007

EVALUATION The following was provided via SDN feedback: I think there's one important exception to the stated goal of not manipulating enum constants by ordinal value, and that is when you're reading protocols. I can think of only three alternatives to looking up the enum constant by its ordinal value in an array of enum constants: (1) embed the name of the enumeration constant itself in the protocol (not an option for me since the protocol is fixed; nor would it be very attractive to new protocol designers). Option (1) is in fact a superior approach. See, for example, the specification for the serialization of enum constants. The reason for this is that enum types must be allowed to evolve while preserving binary compatibility. It is permissible to add, reorder, or remove constants from an enum type without the need to recompile clients, with one proviso: it is not permissible to remove a field that is accessed by a client. This guarantee is violated by baking ordinal values into the specification of a protocol. I do understand that this route is not open to you in your particular case, since the protocol you are implementing is not under your control.
16-06-2006

EVALUATION Note that an enum constant's values() method does not return the same array on each invocation either (for the same reason as getEnumConstants(): arrays cannot be immutable). Enum constants are not generally intended to be manipulated by ordinal value. The ordinal is made available: "for use by sophisticated enum-based data structures, such as EnumSet and EnumMap." It would be hard to square that with a getEnumConstant(int) method in Class, making it convenient to map back and forth between enum constants and their ordinals. Perhaps if the submitter provided more information about the reason for desiring a getEnumConstant(int) method, we could brainstorm a bit on possible alternative approaches. Or maybe it would suggest a compelling reason for adding the method or something functionally equivalent. The workaround using a WeakHashMap has a flaw: the map's values strongly refer to their own keys, so the keys will never be discarded. See 4429536 and its related CRs for a discussion of this issue. Wrapping the values in soft references is one way to patch this up, but that's not without its own problems.
10-06-2006