我試圖Class<?>在運行時檢索型別引數的表示,如下所示:
public <E extends Exception> Try<T> onCatch (Consumer<E> onCatch) {
// retrieve `Class<E>`
}
雖然通常由于型別擦除而無法在運行時檢索型別,但由于反射實用程式和存盤在位元組碼中關于泛型的元資料,它應該可以像我對同一個問題的答案中看到的那樣。
接受的答案如下所示
Class<T> persistentClass = (Class<T>)
((ParameterizedType)getClass().getGenericSuperclass())
.getActualTypeArguments()[0];
但對我不起作用,因為.getActualTypeArguments()[0]不是ParameterizedType但Class因此失敗的演員。
有沒有辦法可靠地做到這一點?我知道我可以手動傳遞Class<E>對該方法的參考,但由于這是一個能夠只使用泛型型別的庫,所以會方便得多。
uj5u.com熱心網友回復:
之所以
Class<T> persistentClass = (Class<T>)
((ParameterizedType)getClass().getGenericSuperclass())
.getActualTypeArguments()[0];
在這個問題的情況下作業:
public static void chill(List<?> aListWithTypeSpiderMan) {
// Here I'd like to get the Class-Object 'SpiderMan'
Class typeOfTheList = ???;
}
public static void main(String... args) {
chill(new ArrayList<SpiderMan>());
}
是因為它的超類ArrayList<SpiderMan>恰好是AbstractList<SpiderMan>,它是一個引數化型別。因此,您可以獲得該型別的實際型別引數。如果將超類的型別引數寫入源檔案中,它們將作為元資料存盤在類檔案中。
但是,在您的情況下,傳遞給onCatch引數的任何內容都不會具有Consumer<T>. 畢竟Consumer<T>不是一個班!您需要使用getGenericInterfaces并找到名稱以 開頭的那個java.util.function.Consumer。
System.out.println(
// I've assumed the Consumer interface is the first one, to keep it brief
((ParameterizedType)onCatch.getClass().getGenericInterfaces()[0])
.getActualTypeArguments()[0]
);
如果呼叫者onCatch這樣呼叫,這將起作用:
onCatch(new Consumer<RuntimeException>() {
@Override
public void accept(RuntimeException e) {
}
});
匿名類將實作Consumer<RuntimeException>,并將此資訊寫入代表匿名類的類檔案中。
但是,如果您使用 lambda:
onCatch((RuntimeException e) -> {});
然后在呼叫者所在的同一個類中只生成一個這樣的方法:
private static void lambda$caller$0(RuntimeException e) {
}
在運行時,invokedynamic用于創建實作的實體,Consumer這是個壞訊息:RuntimeException無論出于何種原因,型別引數都不是該實體生成的類的一部分。
你現在唯一能找到的方法RuntimeException是,如果你以某種方式知道呼叫者是誰,找到lambda$caller$0方法,然后查看它的引數。
也就是說,到目前為止我寫的所有內容幾乎都是實作細節,我不會在生產代碼中使用任何這些。我會說你應該只添加一個Class<E>引數:
onCatch(RuntimeException.class, e -> {});
無論如何,在呼叫者方面看起來并沒有什么不同。
uj5u.com熱心網友回復:
(沒有足夠好的聲譽來回答,但是:)
我認為必須說這不僅僅是一個“一階”繼承問題。如以下示例所示:
abstract class Lifted<T> implements Consumer<T> { public abstract void accept(T x); }
<T> Consumer<T> lift(Consumer<T> f) { return new Lifted<T>() { @Override public void accept(T x) { f.accept(x);}};}
Consumer<String> g = System.out::println;
Consumer<String> c = lift(g);
((java.lang.reflect.ParameterizedType) c.getClass().getGenericSuperclass()).getActualTypeArguments();
上面的示例將列印Type[1] { T }并且不包含對 runtime-type 的任何參考String。
在 JShell 上(尚未研究“標準”運行時), 的型別g是 lambda,其直接超型別是Object. 我還沒有找到它的型別引數。
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/450470.html
