代码之家  ›  专栏  ›  技术社区  ›  Grundlefleck anujkk

如何在Java中将编译的类名与枚举成员匹配?

  •  4
  • Grundlefleck anujkk  · 技术社区  · 15 年前

    在Java中,使用Sun的JDK1.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 类名中的原因是枚举的成员被编译为匿名内部类。我想知道,假设这些类名中使用的数字映射到枚举成员的定义顺序是否安全?如果不是,是否有标准的、有保证的方法从用作匿名类名的数字中找到枚举成员?


    关于枚举的设计,这仅用于说明目的。真正的枚举实现一个接口,每个成员提供不同的方法实现。请不要太在意那些看起来有点奇怪的东西。


    编辑#2

    以编程方式 (比如奇怪的思考废话)。相反,我正在查看堆栈跟踪和分析信息,并尝试将枚举成员上的方法调用(显示为对匿名类的调用)与源代码中的实际枚举成员映射。

    5 回复  |  直到 15 年前
        1
  •  3
  •   meriton    15 年前

    在stacktraces中,您还将获得源文件中的行号。假设你有来源,这将揭示它是哪个常数。(在eclipse中,只需单击console视图中的行号即可直接导航到源代码)。

    要找到类的常量名,可以获取枚举的类文件,并反汇编静态初始值设定项。例如,如果编译:

    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;
    

    但可能有一种更优雅的方法,但如果所有其他方法都失败了,这应该可以做到。

        2
  •  1
  •   Péter Török    15 年前

    我想班级的名字等于 ordinal() + 1

    有效Java第2版,第31项详细解释了为什么 .

        3
  •  1
  •   Alan Geleynse buhbang    15 年前

    你可以做一个比较 MyEnum.FIRST_MEMBER.getClass().getName() 它将为您提供为匿名类生成的名称。

    您还可以使用非匿名类,在这种情况下,您将知道每个类的名称。

    如果您被迫使用匿名类,并且不想在代码中使用,我相信您只需要尝试手动跟踪。

    但是您可以将上面的代码作为调试工具,将其作为助手方法或在调试器中运行。这样您就可以确认哪个匿名类正在接收每个名称。

        4
  •  0
  •   Yishai    15 年前

    一般来说 ordinal() 是个坏主意,因为它很容易改变。

    如果您出于某种原因拥有类名并希望找到相应的枚举值,在我看来最简单的方法是 Class.forName() 在类名上,然后循环遍历枚举成员(使用 values() getClass() 看看它是否等于类对象。

    我还没有测试过上面的内容,但它似乎应该是有效的,并且是可靠的。

        5
  •  0
  •   Mike Deck    15 年前

    首先,对于编译器如何为匿名内部类选择名称,我不认为有任何保证。也就是说,如果这是一种你不希望经常做的一次性分析,那么写一个简单的测试,看看名字是否符合你的预期,我的猜测是这样的。如果您希望在编译器的新版本发布之前完成分析,那么不要太担心它。

    如果您正在寻找一个更长期的解决方案,而您只需要对日志和分析数据进行静态分析,那么为什么不让您的系统在启动时记录每个枚举类型的类名呢?所以不管你有什么鞋带挂钩,你都要做些什么:

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