Why are enums with constant-specifics methods no enums?
I'm working on an application which uses reflection to determine how a certain object should be represented in the GUI.
If an object is an enum it will be represented as a drop-down box.
Yesterday however our code broke on a newly added enum which uses constant-specifics methods.
To illustrate it I'm taking a code example from :
http://java.sun.com/j2se/1.5.0/docs/guide/language/enums.html
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; } },
DIVIDE { double eval(double x, double y) { return x / y; } };
// Do arithmetic op represented by this constant
abstract double eval(double x, double y);
}
Now if you do Operation.PLUS.getClass().isEnum() to my surprise it returns false using 1.5.0_06.
Is this a bug?
[963 byte] By [
isEnuma] at [2007-11-27 11:03:09]

# 1
I've not really been engaged in the new 5.0 features yet, but this is interesting anyway. Tested on java 1.6.0_02-b06 and got the same result; I wonder if this is maybe because the PLUS "enum constant" does not have the properties of a genuine enum type like Operation...?
Also, when compiling the given sample code, javac creates 4 .class files (like Operation$1), presumably one for each constant. These classes have their ACC_ENUM access flag set, thus I expected isEnum() to return true. Try this:
System.out.println("isEnum() == " + Operation.PLUS.getClass().isEnum());
final int ACC_ENUM = 0x4000;
final int mods = Operation.PLUS.getClass().getModifiers();
System.out.println("(mods & ACC_ENUM) != 0: " + ((mods & ACC_ENUM) != 0));
(for ACC_ENUM, see JVMS maintencance information: p. 97, table 4.1 in http://java.sun.com/docs/books/jvms/second_edition/ClassFileFormat-Java5.pdf)
But looking at the Javadoc for isEnum() again, it says:
"Returns true if and only if this class was declared as an enum in the source code."
This is certainly not the case for the synthetic (javac-generated) enum constant classes, so from this point of view, the results are probably correct.
EDIT:
System.out.println("getEnclosingClass().isEnum() == "
+ Operation.PLUS.getClass().getEnclosingClass().isEnum() );
returns true (as expected), so you could use this to determine if a class is an enum constant or not...
Message was edited by:
oebert
# 2
Thanks for the answer.
I already saw this from the Class source code
/**
* Returns true if and only if this class was declared as an enum in the
* source code.
*
* @return true if and only if this class was declared as an enum in the
*source code
* @since 1.5
*/
public boolean isEnum() {
// An enum must both directly extend java.lang.Enum and have
// the ENUM bit set; classes for specialized enum constants
// don't do the former.
return (this.getModifiers() & ENUM) != 0 &&
this.getSuperclass() == java.lang.Enum.class;
}
So indeed it is failing because the the super class is Operations.
Reason for my question is more directed on why they are treated differently.
Is this by design because it has certain implications (?) or was this overlooked.
Code wise it is not an elegant solution:
- change my framework to check the modifiers iso. using isEnum
or
- implement the method using a switch statement
# 3
Oh, looking at the source code would have been an option, yes ... *sigh*
I see that there's a lot to learn when it comes to enums (at least for me). So based on what I believe to know about the matter, I'd say the issue was not overlooked. From a quick experiment, I saw that enum constants without constant-specific methods use an instance of their respective enum subclass as "representation", which in this case is not abstract (contrary to Operation, which is).
So while from a logical point of view, enum constants with and without constant-specific methods should be considered equal(?), implementation-wise, they are not. I think the distinction between enums and enum constants is more of a Java language thing -- the JVM/bytecode is not really aware of it (much like inner classes) -- and is introduced by javac, using little "hacks" to implement the special abilities (which I'll definitely have to investigate when time permits).
After all, isEnum() is invoked on the actual Class object representing the type of an instance. With reflection, you deal with the raw type, no support by javac magic. And indeed, Operation and Operation.PLUS are different types. Operation.PLUS exhibits different behaviour than Operation, and is missing some fields and methods (which could cause certain implications, I guess). Maybe one has to look at isEnum() more from an implementation perspective: isEnum() indicates that the respective class directly supports certain operations (i.e., is declared enum type in the Java source code), while Classes where isEnum() returns false do not. Yeah, the method name is a bit ambiguous, and intuition fools you here, as all the Classes, enums as well as enum constants, are subclasses of java.lang.Enum.
PS: Enum.getDeclaringClass() deals with this issue, too.
EDIT:
> Code wise it is not an elegant solution:
> - change my framework to check the modifiers iso.
>using isEnum
> or
> - implement the method using a switch statement
Class theClass = anObject.getClass();
if (anObject instanceof Enum) {
if ( !theClass().isEnum())
theClass = ((Enum) anObject).getDeclaringClass();
// ...
}
(just a suggestion...)
Message was edited by:
oebert
