Как сопоставить имя скомпилированного класса члену перечисления в Java? - PullRequest
4 голосов
/ 22 октября 2010

В Java с Sun JDK 1.6 с таким перечислением:

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

Скомпилированные файлы:

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

Это также означает, что трассировка стека показывает foo(), или вызов метода, напечатанный в JVisualVM и т. Д., Будет иметь что-то вроде этого вверху:

mypackage.MyEnum$1.run()

* $1 в имени класса происходит из-за членов компилируемого перечисленияанонимным внутренним классам.Мне интересно, можно ли предположить, что числа, используемые в этих именах классов, соответствуют порядку, в котором определены члены перечисления?Если это не так, существует ли стандартный, гарантированный способ найти член enum по номеру, используемому в качестве имени анонимного класса?


EDIT

В отношении дизайнаenum, это использовалось только для иллюстрации.Реальное перечисление реализует интерфейс, и каждый член обеспечивает различную реализацию методов.Пожалуйста, не обращайте слишком много внимания на то, что по общему признанию выглядит немного странным.


РЕДАКТИРОВАТЬ # 2

Чтобы уточнить, я не пытаюсь что-либо делать с этой информацией программно (например, странные бессмысленные отражения).Скорее, я смотрю на трассировки стека и информацию о профилировании и пытаюсь сопоставить вызов метода для члена перечисления (показанный как вызов анонимного класса) с фактическим членом перечисления в исходном коде.

Ответы [ 5 ]

3 голосов
/ 22 октября 2010

В стековых трассах вы также получите номер строки в исходном файле.Если у вас есть источник, это покажет, какая это константа.(В eclipse просто щелкните номер строки в представлении консоли, чтобы перейти непосредственно к источнику).

Чтобы найти имя константы для класса, вы можете получить файл класса для перечисления и разобрать статическийинициализатор.Например, если вы скомпилируете:

enum PieceColor {
    Black {
        @Override public String toString() { return "dark";}
    },
    White {
        @Override public String toString() { return "light";}       
    }
}

и затем выполните:

javap -c PieceColor

, вы получите:

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;

Но может быть более элегантный метод,но если ничего не помогает, это должно сработать.

1 голос
/ 22 октября 2010

Вы можете сделать сравнение с MyEnum.FIRST_MEMBER.getClass().getName(), которое даст вам имя, сгенерированное для анонимного класса.

Порядок именования анонимных классов, вероятно, будет согласованным, но это не гарантируется, поэтомуЯ бы не рекомендовал полагаться на это.

Вы также можете использовать неанонимные классы, в этом случае вы будете знать имя каждого из них.

Если вы вынуждены использовать анонимные классы и делаетеЯ не хочу делать это в коде, я полагаю, что вам просто нужно попытаться отследить вручную.

Но вы могли бы использовать приведенный выше код в качестве инструмента отладки, запустив его как вспомогательный метод или в отладчике.Таким образом, вы можете подтвердить, какой анонимный класс получает каждое имя.

1 голос
/ 22 октября 2010

Для чего вы пытаетесь использовать анонимное имя класса?Если вы предоставите более подробную информацию, мы сможем предложить лучшие решения.

Полагаю, имя класса равно ordinal() + 1.Однако это деталь реализации, которая может изменяться при введении или удалении членов перечисления, поэтому не рекомендуется полагаться на нее.

Эффективная Java, 2-е издание, пункт 31, подробно объясняет, почему лучше использовать поля экземпляров вместо ординалов .

0 голосов
/ 22 октября 2010

Во-первых, я не верю, что существует какая-либо гарантия того, как компилятор выбирает имена для анонимных внутренних классов. При этом, если это однократный тип анализа, который вы не ожидаете делать часто, напишите простой тест, чтобы увидеть, совпадают ли имена так, как вы ожидаете, я думаю, что они делают. Если вы собираетесь закончить профилирование до выхода новой версии компилятора, не беспокойтесь об этом.

Если вы ищете более долгосрочное решение, и все, что вам нужно, это статический анализ журналов и данных профилирования, почему бы не заставить вашу систему регистрировать имена классов для каждого типа перечисления при запуске? Так что внутри любого загрузочного крючка у вас есть что-то вроде:

for(MyEnum value : MyEnum.values()) {
    logger.info(String.format("MyEnum.%s maps to classname %s", value.name(), value.getClass.getName()));
}
0 голосов
/ 22 октября 2010

В общем, делать что-либо с ordinal() - плохая идея, потому что это может так легко измениться.

Если у вас есть имя класса по какой-то причине и вы хотите найти соответствующее значение перечисления, самый простоймне кажется, что нужно сделать Class.forName() для имени класса, а затем перебрать членов перечисления (используя статический метод values()) и вызвать getClass() для каждого и посмотреть, равно ли оно вашему объекту класса.

Я не проверял вышеизложенное, но, похоже, оно должно работать и быть надежным.

...