[英]How to safely cast generic wildcard "?" to known type parameter in Java?
如何安全地將Class<?>
(由Class.forName()
返回)轉換為Class<Annotation>
而不發出“Unchecked cast”警告?
private static Class<? extends Annotation> getAnnotation() throws ClassNotFoundException {
final Class<?> loadedClass = Class.forName("java.lang.annotation.Retention");
if (!Annotation.class.isAssignableFrom(loadedClass)) {
throw new IllegalStateException("@Retention is expected to be an annotation.");
}
@SuppressWarnings("unchecked")
final Class<? extends Annotation> annotationClass = (Class<? extends Annotation>) loadedClass;
return annotationClass;
}
在深入研究答案之前,需要解釋多個誤解。
final Class<Annotation> annotationClass = (Class<Annotation>) loadedClass;
這在任何情況下實際上都是非法的。 試試看:
Class<Number> n = Integer.class;
那不會編譯。
Generics 是不變的。 這意味着在<>
中,您不能使用超類型作為子類型的替代品,反之亦然。
正常 java (當<>
不涉及時)是協變的。 任何子類型都是其超類型之一的替代品。 這個:
Number n = Integer.valueOf(5);
是完全合法的 java。 但在 generics 世界中它不是。 如果你想要它,那么你必須選擇它: X extends Y
是你選擇協方差的方式,而X super Y
是你選擇逆變的方式(逆變好像Integer i = new Number();
是合法 - 超級類型可以代表子類型)。
這都是因為這就是宇宙最終的運作方式。 如果 generics 是自然協變的,這將編譯:
List<Integer> listOfInts = new ArrayList<>();
List<Number> listOfNums = listOfInts;
listOfNums.add(Double.valueOf(1.0));
int i = listOfInts.get(0);
但是,跟隨你自己的眼睛,你就會意識到代碼是一種步行類型違規。 它將非整數推入整數列表。 這就是為什么選擇協變或逆變關門的原因。 如果您選擇協方差,則 add 方法將被禁用*1:
List<? extends Number> list = new ArrayList<Integer>(); //legal
list.add(Integer.valueOf(5)); // will not compile
同樣,如果您選擇逆變, add 效果很好,但get
被禁用。 類型系統意義上的“禁用”:您可以調用它。 但表達式list.get(i)
的類型為 Object:
List<? super Integer> list = new ArrayList<Number>(); // legal
list.add(Integer.valueOf(5)); // legal
Integer i = list.get(0); // won't compile
Object o = list.get(0); // this will.
對於“寫”不完全清楚的類,很難理解為什么Class<Annotation> c = SomeSpecificAnno.class;
應該無法編譯,但確實如此,這是重要的實現之一。
您可以在 java 中制作 class 文字。 這很好用:
Class<? extends Number> c = Integer.class;
That's real java: You can stick .class
at the end of any type and that will be an expression of type java.lang.Class
, in fact, it's of type Class<TheExactThing>
. 所以:
private static Class<? extends Annotation> getAnnotationType() {
return Retention.class;
}
工作和編譯非常好。 我必須更新返回類型,因為如上所述,返回jlClass
的實例,該實例表示指定返回Class<Annotation>
的方法的Retention
注釋與從指定返回的方法返回 integer 一樣損壞一個字符串。
如果您的代碼示例使用java.lang.annotation.Retention
作為替代,但這里的實際字符串是您在編譯時不知道的動態值,則return Retention.class;
選項不在討論范圍內,然后:
private static Class<? extends Annotation> getAnnotationType(String fqn) throws ClassNotFoundException {
return Class.forName(fqn).asSubclass(Annotation.class);
}
同樣,除非沒有其他方法,否則不要使用反射,如果字符串常量中有 class,通常不需要反射。
*1 ) 您可以調用 add,但只能使用 null 文字; list.add(null);
編譯,因為 null 是任何類型的普通有效值。 但是,這當然不是特別有用。
由於類型擦除,泛型類型信息在運行時不再可訪問。 這意味着: Class<?>
和Class<? extends Annotation>
Class<? extends Annotation>
在運行時無法區分-因此您不能進行運行時檢查以使未檢查的強制轉換為已檢查的。
這意味着:你必須忍受警告(注意:警告不是錯誤,它只是意味着“這是有問題的,確保你知道你在做什么。)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.