2010-10-22 30 views
4

在Java中,與Sun的JDK 1.6,用枚舉像這樣:如何將編譯後的類名與Java中的枚舉成員匹配?

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在類名我這是由於枚舉的成員被編譯爲匿名內部類。我想知道是否可以安全地假設這些類名中使用的數字映射到定義枚舉成員的順序?如果不是,是否有一種標準的,有保證的方式來從用作匿名類名的號碼中找到枚舉成員?


EDIT

在問候枚舉的設計,這是僅用於說明的目的。真正的枚舉實現了一個接口,每個成員都提供了不同的方法實現。請不要太在意那些看起來有點奇怪的事情。


編輯#2

爲了澄清,我不是試圖用這些信息編程(如怪異反射廢話)做任何事情。相反,我正在查看堆棧跟蹤和分析信息,並嘗試將枚舉成員(顯示爲匿名類的調用)上的方法調用映射到源代碼中的實際枚舉成員。

回答

3

在蹤跡,你也將獲得在源文件中的行號。假設你有源代碼,那將揭示它是哪個常量。 (在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

你想使用匿名類名稱?如果您提供更多的細節,我們可能會提供更好的解決方案。

我想這個班的名字等於ordinal() + 1。但是,這是一個實現細節,可以在您引入或刪除枚舉成員時進行更改,因此不建議依賴它。

Effetive Java 2nd Edition,Item 31詳細解釋了爲什麼使用實例字段而不是序號更好。

+0

請參閱編輯#2。 – Grundlefleck 2010-10-22 16:35:24

+1

@Grundlefleck,我的猜測是你可以在堆棧跟蹤(在給定的執行運行中)的序號和類名之間進行映射。但是,如果你的枚舉做得太多以至於你需要在剖析器中分析它們的方法調用,那麼你可能會重構將枚舉方法中較重的東西移動到其他適當的命名類中。 – 2010-10-22 16:48:40

1

您可以對MyEnum.FIRST_MEMBER.getClass().getName()進行比較,它會爲您提供爲匿名類生成的名稱。

匿名類命名的順序可能是一致的,但不能保證,所以我不建議依賴它。

你也可以使用非匿名類,在這種情況下你會知道每個類的名字。

如果你被迫使用匿名類而不想在代碼中完成它,我相信你只需要嘗試手動跟蹤。

但是,您可以使用我的上述代碼作爲調試工具,方法是將其作爲輔助方法或調試器運行。這樣你可以確認哪個匿名類正在接收每個名字。

+0

但我無法控制匿名類,這就是編譯器從枚舉定義中生成的內容。另外,請注意,我只想在兩者之間手動映射,而不是在代碼中 - 我沒那麼瘋狂;-) – Grundlefleck 2010-10-22 16:34:52

+0

我在答案中加入了更多的細節。不幸的是,我認爲你將不得不在輔助方法或調試器中使用代碼。我不認爲有任何其他的方式來保證排序。 – 2010-10-22 16:44:28

0

一般做更多的事情與ordinal()是一個壞主意,因爲它可以很容易地改變。

如果您因爲某種原因需要類名並希望找到相應的枚舉值,那麼對我來說最簡單的辦法就是對類名執行Class.forName(),然後遍歷枚舉成員(使用靜態values()方法)並在每個方法上調用getClass()並查看它是否等於您的Class對象。

我沒有測試以上,但似乎它應該工作,並且是可靠的。

0

首先,我不相信這是對編譯器如何選擇匿名內部類名稱的任何保證。話雖如此,如果這是一種你不希望經常做的一次性分析,那就寫一個簡單的測試來看看這些名字是否符合你的期望,但我的猜測是,他們確實這樣做了。如果你希望在編譯器的新版本出來之前完成你的分析,那麼不要太擔心。

如果您正在尋找多一點的時間長期的解決方案,所有你需要它是日誌的靜態分析和分析數據,爲什麼不把你的系統日誌的類名在啓動時每個枚舉類型?因此,無論你在做什麼bootstrap鉤做你喜歡的東西:

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