java 枚舉類定義靜態(tài)valueOf(java.lang.String)方法的問題及解決
問題的起因
起因來自于我對于java枚舉類的無知。
我本來想定義這樣一個枚舉類:
public enum MenuOptions {
CHAT_ROOM("#1"),
MENU("#0"),
ERROR("#9999");
private String value;
MenuOptions(String value) {
this.value = value;
}
@Override
public String toString() {
return value;
}
// 根據(jù)字符串的值返回枚舉常量
public static MenuOptions valueOf(String value) {
//...
return MenuOptions.ERROR;
}
}
關鍵是定義的這個方法:

我用的IDE是IDEA,jdk版本是1.8,但是編譯版本、語言級別是1.7
這里報方法定義已存在,,,???
這個時候,想起來枚舉類有一個valueOf方法的,傳入的參數(shù)是枚舉常量的變量名,返回這個枚舉常量,然后debug,發(fā)現(xiàn)枚舉類內(nèi)部調用了java.lang.Enum類的這個方法:
public static <T extends Enum<T>> T valueOf(Class<T> enumType,
String name) {
T result = enumType.enumConstantDirectory().get(name);
if (result != null)
return result;
if (name == null)
throw new NullPointerException("Name is null");
throw new IllegalArgumentException(
"No enum constant " + enumType.getCanonicalName() + "." + name);
}
猜測、分析
猜測可能是編譯的時候,這個類被JDK默認繼承了Enum類,通過IDEA查看class反編譯文件:

what???什么也沒有。。。
使用javap來看這個class文件:

首先,確實可以看到這個類繼承自Enum類,然后這個類valueOf(String)調用了Enum.valueOf(Class,String)方法:
![]()
but,這個類的valueOf(String)在Enum類里沒有找到(下面是Enum類的所有方法聲明):

這樣看來是JDK編譯的時候,動態(tài)增加的,不知道猜想是否準確,回頭找個時間好好查下資料了解下。
最終解決方案
碰見這樣的,我也不知道怎么解決了,本來我這個方法,是想通過傳入的值返回相應的枚舉值,不是像默認的那樣傳入枚舉量的變量名字符串。
那這樣的話,我只能換個名了,,,尷尬。。。如下完整代碼 :
public enum MenuOptions {
CHAT_ROOM("#1"),
MENU("#0"),
ERROR("#9999"){
{
innerMap.put("#1", "CHAT_ROOM");
innerMap.put("#0", "MENU");
innerMap.put("#9999", "ERROR");
}
};
private String value;
protected Map<String, String> innerMap = new HashMap<>();
MenuOptions(String value) {
this.value = value;
}
@Override
public String toString() {
return value;
}
// 根據(jù)字符串的值返回枚舉常量
public static MenuOptions valueOf2(String value) {
String name = MenuOptions.ERROR.innerMap.get(value.trim().intern());
if (name == null) {
return MenuOptions.ERROR;
}
MenuOptions option = valueOf(name);
return option;
}
}
枚舉類Enum方法簡介(valueof,value,ordinal)
Enum作為Sun全新引進的一個關鍵字,看起來很象是特殊的class, 它也可以有自己的變量,可以定義自己的方法,可以實現(xiàn)一個或者多個接口。 當我們在聲明一個enum類型時,
我們應該注意到enum類型有如下的一些特征
1.它不能有public的構造函數(shù),這樣做可以保證客戶代碼沒有辦法新建一個enum的實例。
2.所有枚舉值都是public , static , final的。注意這一點只是針對于枚舉值,我們可以和在普通類里面定義 變量一樣定義其它任何類型的非枚舉變量,這些變量可以用任何你想用的修飾符。
3.Enum默認實現(xiàn)了java.lang.Comparable接口。
4.Enum覆載了了toString方法,因此我們?nèi)绻{用Color.Blue.toString()默認返回字符串”Blue”.
5.Enum提供了一個valueOf方法,這個方法和toString方法是相對應的。調用valueOf(“Blue”)將返回Color.Blue.因此我們在自己重寫toString方法的時候就要注意到這一點,一把來說應該相對應地重寫valueOf方法。
6.Enum還提供了values方法,這個方法使你能夠方便的遍歷所有的枚舉值。
7.Enum還有一個oridinal的方法,這個方法返回枚舉值在枚舉類種的順序,這個順序根據(jù)枚舉值聲明的順序而定,這里Color.Red.ordinal()返回0。
了解了這些基本特性,我們來看看如何使用它們
1.遍歷所有有枚舉值. 知道了有values方法,我們可以輕車熟路地用ForEach循環(huán)來遍歷了枚舉值了。
for (Color c: Color.values()) System.out.println(“find value:” + c);
2.在enum中定義方法和變量,比如我們可以為Color增加一個方法隨機返回一個顏色。
public enum Color {
Red,
Green,
Blue;
/*
*定義一個變量表示枚舉值的數(shù)目。
*(我有點奇怪為什么sun沒有給enum直接提供一個size方法).
*/
private static int number = Color.values().length ;
/**
* 隨機返回一個枚舉值
@return a random enum value.
*/
public static Color getRandomColor(){
long random = System.currentTimeMillis() % number;
switch ((int) random){
case 0:
return Color.Red;
case 1:
return Color.Green;
case 2:
return Color.Blue;
default : return Color.Red;
}
}
}
可以看出這在枚舉類型里定義變量和方法和在普通類里面定義方法和變量沒有什么區(qū)別。唯一要注意的只是變量和方法定義必須放在所有枚舉值定義的后面,否則編譯器會給出一個錯誤。
3.覆載(Override)toString, valueOf方法
前面我們已經(jīng)知道enum提供了toString,valueOf等方法,很多時候我們都需要覆載默認的toString方法,那么對于enum我們怎么做呢。其實這和覆載一個普通class的toString方法沒有什么區(qū)別。
….
public String toString(){
switch (this){
case Red:
return "Color.Red ";
case Green:
return "Color.Green ";
case Blue:
return "Color.Blue ";
default:
return "Unknow Color ";
}
}
….
這時我們可以看到,此時再用前面的遍歷代碼打印出來的是
Color.Red
Color.Green
Color.Blue
而不是
Red
Green
Blue.
可以看到toString確實是被覆載了。一般來說在覆載toString的時候我們同時也應該覆載valueOf方法,以保持它們相互的一致性。
4.使用構造函數(shù)
雖然enum不可以有public的構造函數(shù),但是我們還是可以定義private的構造函數(shù),在enum內(nèi)部使用。還是用Color這個例子。
public enum Color {
Red( "This is Red "),
Green( "This is Green "),
Blue( "This is Blue ");
private String desc;
Color(String desc){
this.desc = desc;
}
public String getDesc(){
return this.desc;
}
}
這里我們?yōu)槊恳粋€顏色提供了一個說明信息, 然后定義了一個構造函數(shù)接受這個說明信息。
要注意這里構造函數(shù)不能為public或者protected, 從而保證構造函數(shù)只能在內(nèi)部使用,客戶代碼不能new一個枚舉值的實例出來。這也是完全符合情理的,因為我們知道枚舉值是public static final的常量而已。
5.實現(xiàn)特定的接口
我們已經(jīng)知道enum可以定義變量和方法,它要實現(xiàn)一個接口也和普通class實現(xiàn)一個接口一樣,這里就不作示例了。
6.定義枚舉值自己的方法。
前面我們看到可以為enum定義一些方法,其實我們甚至可以為每一個枚舉值定義方法。這樣,我們前面覆載 toString的例子可以被改寫成這樣。
public enum Color {
Red {
public String toString(){
return "Color.Red ";
}
},
Green {
public String toString(){
return "Color.Green ";
}
},
Blue{
public String toString(){
return "Color.Blue ";
}
};
}
從邏輯上來說這樣比原先提供一個“全局“的toString方法要清晰一些。
總的來說,enum作為一個全新定義的類型,是希望能夠幫助程序員寫出的代碼更加簡單易懂,個人覺得一般也不需要過多的使用enum的一些高級特性,否則就和簡單易懂的初衷想違背了。希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
SpringBoot中創(chuàng)建bean的7種方式總結
Spring是一款廣泛應用于企業(yè)級應用程序開發(fā)的Java框架,其 IOC 和 DI 特性可以有效地管理應用程序中的對象,提高了應用程序的可維護性和可擴展性,那你知道spring有哪些方式將bean放入容器嘛,今天就給大家總結一下2023-07-07
SpringBoot監(jiān)控SQL運行情況的流程步驟
Druid是Java語言中最好的數(shù)據(jù)庫連接池,雖然?HikariCP?的速度稍快,但是,Druid能夠提供強大的監(jiān)控和擴展功能?,也是阿里巴巴的開源項目,本文給大家介紹了SpringBoot監(jiān)控SQL運行情況的流程步驟,需要的朋友可以參考下2024-03-03
List轉變?yōu)槎禾柗指舻腟tring(Java7和Java8分別實現(xiàn))
這篇文章主要介紹了Java7和Java8分別實現(xiàn)List轉變?yōu)槎禾柗指舻腟tring,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-06-06
IDEA手動添加junit4時出現(xiàn)的問題與解決方法
這篇文章主要給大家介紹了關于IDEA手動添加junit4時出現(xiàn)的問題與解決方法,文中通過圖文介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2021-03-03

