How to map compiled class name to enum member in Java?

In Java, with Sun JDK 1.6, with an enumeration such as this:

public enum MyEnum { FIRST_MEMBER { public void foo() { } }, SECOND_MEMBER { public void foo() { } }, THIRD_MEMBER { public void foo() { } }; } 

Compiled files:

 MyEnum$1.class MyEnum$2.class MyEnum$3.class MyEnum.class 

It also means that a stack trace showing foo() or a method call printed in JVisualVM etc. will have something like this at the top:

 mypackage.MyEnum$1.run() 

$1 in the class name is because enumeration members are compiled into anonymous inner classes. I wonder if it can be assumed that the numbers used in these class names correspond to the order in which the members of the enum are defined? If this is not the case, is there a standard guaranteed way to find an enumeration member from a number used as the name of an anonymous class?


EDIT

As for the listing design, this was used for illustration only. The real enum implements the interface, and each member provides a different implementation of the methods. Please don't pay too much attention to what admittedly looks a bit odd.


CHANGE No. 2

To clarify, I am not trying to do anything with this information programmatically (for example, a strange absurd absurdity). Rather, I look at the stack trace and profiling information and try to map a method call to an enumeration member (shown as an anonymous class call) against the actual enumeration member in the source code.

+4
source share
5 answers

In stacktraces you will also get the line number in the source file. Assuming you have a source, this will show which one. (In eclipse, simply click the line number in the console view to go directly to the source file).

To find a constant name for a class, you can grab the class file for enumeration and parse the static initializer. For example, 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 are getting:

 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 may be a more elegant method, but if all else fails, this should do the trick.

+3
source

What are you trying to use the name of an anonymous class for? If you give more detailed information, we can offer the best solutions.

I think the class name is ordinal() + 1 . However, this is an implementation detail that may change when entering or deleting members of an enumeration, so relying on it is not recommended.

Effetive Java 2nd Edition, article 31 explains in detail why it is better to use instance fields instead of ordinals.

+1
source

You can do a comparison with MyEnum.FIRST_MEMBER.getClass().getName() , which will give you the name generated for the anonymous class.

The name order of anonymous classes is likely to be consistent, but it is not guaranteed, so I would not recommend relying on it.

You can also use non-anonymous classes, in which case you should know the name of each of them.

If you are forced to use anonymous classes and do not want to do this in code, I believe that you just need to try tracking manually.

But you can use my code above as a debugging tool by running it as a helper method or debugger. This way you can confirm which anonymous class gets each name.

+1
source

In general, doing anything with ordinal() is a bad idea because it can easily change.

If you have a class name for any reason and want to find the appropriate enumeration value, it seems easiest to make Class.forName() in the class name and then skip the enumeration elements (using static values() ) and call getClass() for each and see if it is equal to your Class object.

I have not tested the above, but it looks like it should work and be reliable.

0
source

First, I do not believe that there is any guarantee that the compiler selects names for anonymous inner classes. At the same time, if this is a one-time type of analysis that you do not expect to do often, write a simple test to find out if the names match, as you expect, I assume they do it. If you plan on profiling before a new version of the compiler is released, don't worry about that.

If you are looking for a longer term solution and all you need is a static analysis of logs and profiling data, why don't you register system names for each type of enumeration at startup? Therefore, inside any boot hook you are doing something like:

 for(MyEnum value : MyEnum.values()) { logger.info(String.format("MyEnum.%s maps to classname %s", value.name(), value.getClass.getName())); } 
0
source

Source: https://habr.com/ru/post/904153/


All Articles