我在我的 Java 程式中撰寫了這個函式:
public <T extends Enum<T>> T getEnum(Class<T> enumClass, String name) {
String value = get(name);
if (value == null) return null;
if (value.length() == 0) return null;
return Enum.valueOf(enumClass, value);
}
這就是我所說的:
CollegeDegreeType degreeType = model.getEnum(CollegeDegreeType.class, "degree_type");
它有效,但我覺得我應該能夠省略第一個引數并從回傳型別(分配結果的型別)推斷它。這可能嗎?如果是這樣,語法是什么?
uj5u.com熱心網友回復:
讓我問你這個問題:如果沒有泛型,你將如何做到這一點?如果沒有泛型你不能做到,那么你也不能用泛型來做到這一點。泛型在編譯時被洗掉并且在運行時不存在(除了類、欄位和方法宣告中的泛型可以在運行時從類檔案中檢查,但這與這里無關)。因此,如果可以使用泛型完成,您必須能夠擦除它并在沒有泛型的情況下完成。
洗掉后的原始方法如下所示:
public Enum getEnum(Class enumClass, String name) {
String value = get(name);
if (value == null) return null;
if (value.length() == 0) return null;
return Enum.valueOf(enumClass, value);
}
但是你希望能夠做這樣的事情:
public Enum getEnum(String name) {
String value = get(name);
if (value == null) return null;
if (value.length() == 0) return null;
return ???
}
這是不可能的。即使 Java 沒有泛型,Enum.valueOf()
仍然需要enumClass
在運行時使用,因為這就是它知道在哪個列舉中查找名稱的方式。事實上,可以有多個具有相同名稱的常量的列舉,并且EnumA.SOME_CONSTANT
會有所不同運行時的物件 from EnumB.SOME_CONSTANT
,那么它怎么知道你想要哪一個呢?它不能。
也許您認為泛型方法以某種方式傳遞了它的泛型引數(此處為T
)。但這不是泛型方法的含義。當您撰寫一個泛型 on 的泛型方法時T
,這意味著您的方法的運行時位元組碼必須對任何T
(在 的范圍內T
)都是正確的,而不知道是T
什么。呼叫者可以期望你的方法無論如何都會起作用T
(事實上,呼叫者甚至可能不知道是什么T
)。
uj5u.com熱心網友回復:
它有效,但我覺得我應該能夠省略第一個引數并從回傳型別推斷它。
T
除非您有以下任何一項,否則您將無法在運行時提供:
- 型別的方法引數
T
(您要省略); - 或者一個實體
T
是方法呼叫的結果; - 列舉型別的硬編碼實體(坦率地說,這毫無意義)。
示例(第二個和第三個選項):
public static <T extends Enum<T>> T getEnum(String name) {
T myEnum = (T) getHardCodedEnumMember();
return (T) Enum.valueOf(myEnum.getClass(), name);
}
public static <T extends Enum<T>> T getHardCodedEnumMember() {
return (T) DayOfWeek.SATURDAY;
}
這會起作用,但我想這不是你的意圖,所以我必須堅持將Class
實體作為引數傳遞。
要考慮的另一件事是,您列出的方法可能會產生三種結果:
- 它可以回傳一個列舉元素;
- 它可能會回傳
null
值; - 或拋出例外。
如果沒有匹配名稱的列舉成員,方法valueOf()
將拋出。事先檢查和空字串不會讓你從中受益。同時,你需要處理一個可以為空的結果。IllegalArgumentException
null
您可以通過消除運行時例外并將可能的結果減少為空的可選項或包含結果的可選項來采取另一種方法。為此,您可以使用EnumSet.allOf()
檢索指定列舉型別的所有內容。
public <T extends Enum<T>> Optional<T> getEnum(Class<T> enumClass, String name) {
String value = get(name);
if (value == null || value.length() == 0) {
return Optional.empty();
}
return EnumSet.allOf(enumClass).stream()
.filter(element -> element.name().equals(value))
.findFirst();
}
uj5u.com熱心網友回復:
由于泛型型別擦除,該型別T
僅在編譯時才知道,這會阻止您在運行時訪問它需要的類Enum.valueOf
。
請參閱https://docs.oracle.com/javase/tutorial/java/generics/erasure.html
在某些情況下,在技術上可以檢索泛型型別,請參閱在運行時獲取類的泛型型別
但我認為這不會提高代碼的質量。在我看來,您目前獲得課程的解決方案是一種很好的方法。
一個可行的示例實作,但在我看來是 hacky*(*由于未經檢查的強制轉換、反射和匿名類實作/擴展語法要求):
public abstract class MyEnumParser<T extends Enum<T>> {
public T parseEnum(String value) {
return Enum.valueOf(getEnumClass(), value);
}
private Class<T> getEnumClass() {
return (Class<T>) ((ParameterizedType) getClass()
.getGenericSuperclass()).getActualTypeArguments()[0];
}
}
用法:
MyEnumParser<CollegeDegreeType> parser = new MyEnumParser<>() {};
System.out.println(parser.parseEnum("degree_type"));
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/470414.html
下一篇:使用型別作為通用方法引數