簡體   English   中英

使用泛型時,Java反射獲取運行時類型

[英]Java reflection get runtime type when using generics

我想知道如何獲得程序員在使用泛型時編寫的運行時類型。 例如,如果我有class Main<T extends List<String>>並且程序員寫了類似的東西

Main<ArrayList<String>> main = new Main<>();

如何使用反射使用擴展List<String>類?

我只是好奇我怎樣才能做到這一點。

main.getClass().getTypeParameters()[0].getBounds[] 

我只能理解邊界類(不是運行時類)。

正如上面的評論所指出的,由於類型擦除,你無法做到這一點。 但在評論中,后續問題是:

我知道編譯后刪除了泛型,但我想知道ClassCastException是如何拋出運行時的? 對不起,如果這是一個愚蠢的問題,但如果沒有關於類的任何信息,它如何知道拋出此異常。

答案是,雖然類型參數從類型中刪除,但它仍然保留在字節碼中

從本質上講,編譯器會轉換為:

List<String> list = new ArrayList<>();
list.add("foo");
String value = list.get(0);

進入這個:

List list = new ArrayList();
list.add("foo");
String value = (String) list.get(0); // note the cast!

這意味着String類型不再與字節碼中的ArrayList類型相關聯,但它仍然出現(以類轉換指令的形式)。 如果在運行時類型不同,您將獲得ClassCastException

這也解釋了為什么你可以逃避這樣的事情:

// The next line should raise a warning about raw types
// if compiled with Java 1.5 or newer
List rawList = new ArrayList();

// Since this is a raw List, it can hold any object.
// Let's stick a number in there.
rawList.add(new Integer(42));

// This is an unchecked conversion. Not always wrong, but always risky.
List<String> stringList = rawList;

// You'd think this would be an error. But it isn't!
Object value = stringList.get(0);

事實上,如果你嘗試它,你會發現你可以安全地將42值作為一個Object拉回來,而根本沒有任何錯誤。 這樣做的原因是,編譯器不會插入投以String在這里-它只是插入一個強制轉換為Object (因為左側類型只是Object ),並從投IntegerObject成功,因為它應該。

無論如何,這只是一種冗長的解釋方式,即類型擦除不會擦除對給定類型的所有引用,只會刪除類型參數本身。

事實上,作為這里提到的一個現在刪除的答案,您可以通過一種名為Gafter's Gadget的技術利用這種“殘留”類型信息,您可以使用ParameterizedType上的getActualTypeArguments()方法訪問該技術。

小工具的工作方式是創建參數化類型的空子類,例如new TypeToken<String>() {} 由於這里的匿名類是具體類型的子類(這里沒有類型參數T ,它已被實際類型, String替換)類型上的方法必須能夠返回實數類型(在本例中為String ) 。 使用反射你可以發現這種類型:在這種情況下, getActualTypeParameters()[0]將返回String.class

Gafter的小工具可以擴展到任意復雜的參數化類型,並且實際上經常被使用反射和泛型做大量工作的框架使用。 例如,Google Guice依賴注入框架有一個名為TypeLiteral的類型,它正是為此目的服務的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM