In Java, with Sun's JDK 1.6, with an enum such as this:
public enum MyEnum {
FIRST_MEMBER { public void foo() { } },
SECOND_MEMBER { public void foo() { } },
THI开发者_Go百科RD_MEMBER { public void foo() { } };
}
The compiled files are:
MyEnum$1.class MyEnum$2.class MyEnum$3.class MyEnum.class
This also means that a stack trace showing foo()
, or the method call printed in JVisualVM, etc., will have something like this at the top:
mypackage.MyEnum$1.run()
The $1
in the class name is due to the members of the enum being compiled to anonymous inner classes. I am wondering if it is safe to assume that the numbers used in these class names map to the order in which the enum members are defined? If it is not, is there a standard, guaranteed way to find the enum member from the number used as the anonymous class name?
EDIT
In regards to the design of the enum, this was used for illustration purposes only. The real enum implements an interface, and each member provides a different implementation of the methods. Please don't pay too much attention to what admittedly looks a bit strange.
EDIT #2
To clarify, I am not trying to do anything with this information programmatically (such as weird reflection nonsense). Rather, I am looking at stack traces and profiling information, and trying to map a method call on an enum member (shown as a call on an anonymous class) against the actual enum member in the source code.
In stacktraces, you'll also get the line number in the source file. Assuming you have the source, that'll reveal which constant it is. (In eclipse, simply click the line number in the console view to directly navigate to the source).
To find the constant name for a class, you could grab the class file for the enum, and disassemble the static initializer. For instance, if you compile:
enum PieceColor {
Black {
@Override public String toString() { return "dark";}
},
White {
@Override public String toString() { return "light";}
}
}
and then do:
javap -c PieceColor
you get:
static {};
Code:
0: new #13; //class tools/PieceColor$1
3: dup
4: ldc #15; //String Black
6: iconst_0
7: invokespecial #16; //Method tools/PieceColor$1."<init>":(Ljava/lang/String;I)V
10: putstatic #20; //Field Black:Ltools/PieceColor;
13: new #22; //class tools/PieceColor$2
16: dup
17: ldc #24; //String White
19: iconst_1
20: invokespecial #25; //Method tools/PieceColor$2."<init>":(Ljava/lang/String;I)V
23: putstatic #26; //Field White:Ltools/PieceColor;
But there might be a more elegant method, but if all else fails, this should do the trick.
What are you trying to use the anonymous class name for? If you give more details, we may be able to offer better solutions.
I guess the class' name equals to ordinal() + 1
. However, this is an implementation detail, which can change as you introduce or remove enum members, so it is not recommended to rely on it.
Effetive Java 2nd Edition, Item 31 explains in details why is it better to use instance fields instead of ordinals.
You can do a comparison against MyEnum.FIRST_MEMBER.getClass().getName()
which will give you the name generated for the anonymous class.
The order that the anonymous classes are named will probably be consistent, but it is not guaranteed, so I would not recommend relying on it.
You also could use non-anonymous classes, in which case you would know the name of each.
If you are forced to use anonymous classes and do not want to do it in code, I believe you will just need to try to keep track manually.
But you could use my above code as a debugging tool by running it as a helper method or in a debugger. That way you can confirm which anonymous class is receiving each name.
In general doing much of anything with ordinal()
is a bad idea because it can change so easily.
If you have the class name for some reason and want to find the corresponding enum value, the easiest seems to me to be to do a Class.forName()
on the class name, and then loop through the enum members (using the static values()
method) and call getClass()
on each and see if it equals your Class object.
I haven't test the above, but it seems it should work, and be reliable.
First, I don't believe there is any guarantee about how the compiler chooses names for anonymous inner classes. That being said, if this is a one-time type of analysis that you don't expect to do often, write a simple test to see if the names match up they way you expect, my guess is that they do. If you expect to be done with your profiling before a new version of the compiler comes out, then don't worry too much about it.
If you are looking for a bit longer term solution and all you need it for is static analysis of logs and profiling data, why not have your system log the class names for each enum type at startup? So inside whatever bootstrap hook you have do something like:
for(MyEnum value : MyEnum.values()) {
logger.info(String.format("MyEnum.%s maps to classname %s", value.name(), value.getClass.getName()));
}
精彩评论