[英]Static polymorphism with generics in Java
我希望創建一些干凈的代碼,可以遞歸地挖掘集合並打印它找到的第一個Integer。 來自C ++背景的方法看起來像這樣:
class Test {
static void print(Integer i) {
System.out.println(i);
}
static <T> void print(ArrayList<T> arr) {
T entry= arr.at(0);
Test.print (entry);
}
static void Test() {
ArrayList<ArrayList<Integer>> arrayOfArrayOfInt= create();
print( arrayOfArrayOfInt );
}
}
不幸的是,這不起作用。
一種替代方法是放棄靜態多態並創建函數print(Object o),然后執行一些instanceof檢查以分支到正確的行為。 這是由於Java的類型擦除是否可以完成的唯一方法,還是有更優雅的方法?
你是正確的假設你不能像處理c ++模板一樣對待java泛型。
因此,如果沒有instanceof
,這將不會猜測條目是一個整數(既不在運行時也不在編譯時):
static <T> void print(ArrayList<T> arr) {
T entry= arr.get(0);
Test.print (entry);
}
所以要“創建一些干凈的代碼,可以遞歸地挖掘集合並打印它找到的第一個整數” (或者更確切地說是索引0處的第一個非列表元素):
static void print(Object obj) {
System.out.println(obj);
}
static <T> void print(List<T> arr) {
T entry = arr.get(0);
if (entry instanceof List)
print((List<?>) entry);
else
print(entry);
}
static void test() {
List<List<Integer>> arrayOfArrayOfInt = create();
print(arrayOfArrayOfInt);
}
不是您想要的優雅解決方案,而是泛型系統所需的可行解決方案。
下面的方法將遞歸挖成一個Object
,並返回一個Optional
含有第一Integer
它找到,或Optional.empty()
如果不能找到一個。
static Optional<Integer> firstInt(Object o) {
if (o instanceof Integer)
return Optional.of((Integer) o);
if (o instanceof int[]) {
int[] array = (int[]) o;
return array.length > 0 ? Optional.of(array[0]) : Optional.empty();
}
if (o instanceof Object[])
return firstInt(Arrays.asList((Object[]) o));
if (o instanceof Iterable) {
for (Object o2 : (Iterable<?>) o) {
Optional<Integer> result = firstInt(o2);
if (result.isPresent())
return result;
}
}
return Optional.empty();
}
你可以使用多態來做到這一點,但是你必須創建一個像這樣的Searchable
接口
interface Searchable {
Optional<Integer> search();
}
然后為您希望能夠搜索的所有具體類型創建包裝類。 例如:
public final class SearchableList<T extends Searchable> extends AbstractList<T> implements Searchable {
private final List<T> list;
SearchableList(List<T> list) {
this.list = list;
}
// rest omitted
}
然而,這將是一個令人費解的混亂,我會避免它,而不是更喜歡檢查instanceof
。
簡答:是的,放棄靜態多態並使用instanceof
。 或者只是寫一個本地循環來完成你的工作。
Java的泛型比C ++的模板弱得多。 特別是,編譯器不會為您生成新的特化。 您可以用它們做的就是對已經通過常規方法定義的類和方法進行更嚴格的靜態類型檢查(例如,您手動編寫它們或使用工具生成它們)。 如您所料,類型擦除是相關的。
使用Java泛型,此代碼:
static <T> void print(ArrayList<T> arr) {
T entry= arr.at(0);
Test.print (entry);
}
由於類型擦除,總是有效地轉入此:
static Object void print(ArrayList<Object> arr) {
Object entry= arr.at(0);
Test.print (entry);
}
它不會像C ++模板那樣產生單獨的特化。
您的特定代碼無法正常工作,因為它缺少一些特定的方法,您必須手動提供這些方法。 但我想我能看到你想要去的地方。 (我假設您希望最終得到的代碼可以在更廣泛的類型上工作,而不僅僅是List<List<Integer>>
。)並且您不會使用Java泛型獲得滿意的解決方案。 (你可以使它工作,但你必須手工編寫大量的代碼,哪些模板對你沒用。而且會有一些grody edge例。)切換到動態類型檢查和instanceof
; 這是Java方法來做這些事情。
首先,獲取元素的函數是“get”而不是“at”。 但是,你的主要問題是你試圖調用帶有整數的函數“print”,但是傳遞一個類型為“T”的值。
如果要調用print並將其傳遞給未知類型的值,則該函數必須采用Object類型的參數或采用泛型參數,例如
public <T> void print(T item)
要么
public void print (Object item)
Java只會在編譯時將函數調用與最具體的重載進行匹配,如果您在運行時嘗試查找正確的重載,則必須使用instanceof。 或者,您可以構建一個從Class對象指向Consumer對象的HashMap。
HashMap<Class<?>, Consumer<?>> prints = new HashMap<>();
void setup() {
prints.put(ArrayList.class, (list) -> {
for (Object o : list) {
print(o);
}
}
prints.put(Integer.class, (integer) -> System.out.println(integer));
}
void print(Object o) {
Consumer<?> func = prints.get(o.getClass());
func.accept(o);
}
如果要保持此代碼的方式,則必須為對象添加另一種打印方法。
public static void print(Object o){}
但無論如何,你的例子有更好的方法。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.