我有以下界面:
public interface Message< T> {
//some other methods[/span
Class<T> getType()。
}
每個實作總是回傳T的類。例如:
public class StringMessage implements Message< String> {
//其他重寫
@Override
Class<String> getType() {
return String.class。
}
我想讓這個getType成為介面的default方法,但這是不可能的,因為我不能呼叫T.class,因為型別被清除了:
public interface Message< T> {
//some other methods
default Class<T> getType() {
return T.class; //<-- not allowed }
}
}
你知道有什么技巧可以避免在每個實作上重復return XXX.class嗎?
注意,我不能將T系結到任何東西上,它必須保持無界。
注1:如果答案是否定的,并且得到了很好的解釋,我將接受它。
注2:我發現很多問題 "聽起來像這樣",但并不完全相同(它們通常指的是實體而不是靜態介面本身)。如果你找到了正確的重復問題,請不要猶豫,將其標記為正確的問題,我會將其洗掉。
uj5u.com熱心網友回復:
是的。而且不,這很復雜。
在我們走這條路之前,你確定嗎?
一般來說,關心Class<T>中的<T>是一種代碼氣味,意味著你的API設計很糟糕。例如,泛型中的T可以不表示基元,但是它可以表示引數化型別;例如,Stream<List<? extends Foo & Bar>>就很好。另一方面,java.lang.Class的一個實體可以表示基元(return int.class;),但是不能表示引數化型別。List<String>.class不是一個東西,也沒有j.l.Class的實體來表示 "字串的串列"。List就到此為止了。
一般來說,如果你認為你想要一個類的實體,你真正想要的是一個工廠。工廠是對建構式進行抽象的方法。而不是回傳一個Class<T>,這段代碼可能應該是想要一個Supplier<T>,或者其他一些T的介面,來完成你目前使用的Class<?>的作業。如果你正在使用該類實體來呼叫.getConstructors(),那么就將該邏輯移到一個介面中。等等。
你已經考慮了所有這些,但仍然堅持。
泛型在運行時被清除,是的,但是它們仍然存在于那些它們是簽名的一部分的地方。換句話說,在類定義的extends和implements條款中,在欄位的型別中,以及在任何方法定義的引數和回傳型別中。JVM會考慮這些注釋(JVM絲毫不知道或不關心泛型,這純粹是javac和編輯器擔心的事情),但是它們是在類檔案中可用,因此理論上你至少可以查詢它。
但是,這一點非常重要,只有在編譯(寫)時存在的字面內容才是可用的。
所以,是的,你可以在public class StringMsg implements Message<String>中檢索到String位。但是如果你寫public class GeneralMsg<T> implements Message<T>呢?那么你得到的就是T。如果你寫public class ListOfStringsMessage implements Message<List<String>>呢?你可以在這里獲得List<String>,但是這個概念不能用java.lang.Class型別的值來傳達。
這樣做的方法是使用.getGenericInterfaces()方法。但是,這是一個非常低級的方法,它只是讓你得到你所要求的東西。你所呼叫的類直接實作的介面串列(保留了任何型別引數)。因此,你需要寫一大堆的代碼。畢竟,也許你有這樣的東西:
class StringMessage implements Message<。 String> {}。
class UnicornStringMessage extends StringMessage {}.
或者甚至
interface StringMessage implements Message< String> {}。
class MyStringMessage implements StringMessage {}。
你需要寫大量的代碼來遍歷整個類的層次結構。因此,在這個答案中把它全部寫出來是一個太遠的橋梁。這只是處理最簡單的情況,其他的都會失敗。因此,你需要使用這段代碼并對其進行擴展,并添加:
class GenericMsg<T> implements Message<T> .Message<Concrete> 的介面,然后擁有一個實作子介面的類,來檢測層次性的使用(或者,如果你喜歡的話,用一個適當的訊息來禁止它)。
interface SubIntf<T> implements Message<T>,然后有一個class Foo implements SubIntf<String>。
class MyMsg implements Message<String> class MySubMsg extends MyMsg。考慮到這一點:
default Class<T> getType() {
for (Type i : getClass().getGenericInterfaces() ) {
if (! (i instanceof ParameterizedType pt) 繼續。
if (!pt.getRawType().equals(Message.class)) continue。
Type param = pt.getActualTypeArguments[0] 。
if (param instanceof Class<?> paramC) return paramC;
}
throw new IllegalArgumentException("程式員錯誤。" getClass() " 必須實作Message<ConcreteType>")。)
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/318412.html
標籤:
上一篇:安卓串列視圖不能與片段作業
