簡體   English   中英

Java泛型函數:如何返回泛型類型

[英]Java generic function: how to return Generic type

這是一個Java通用模式:

public <T> T getResultData(Class<T> resultClass, other_args) { 
   ...
   return resultClass.cast(T-thing);
}

典型的通話看起來像:

   DoubleBuffer buffer;
   buffer = thing.getResultData(DoubleBuffer.class, args);

當期望的返回類型本身是通用的時候,我從來沒有弄清楚如何干凈地使用這個模式。 要具體化,如果像這樣的函數想要返回Map<String,String>怎么辦? 因為你無法獲得泛型的類對象,當然,唯一的選擇是傳遞Map.class ,然后你需要一個@SuppressWarning和一個@SuppressWarning

一個人最終會打電話:

Map<String, String> returnedMap;
returnedMap = thing.getResultData(Map.class, some_other_args);

現在回到需要強制轉換並抑制警告。

我想可以從java.lang.reflect.Type系列而不是Class獲取一些東西,但這些並不是特別容易編造的。 看起來像:

class Dummy {
 Map<String, String> field;
}

...

Type typeObject = Dummy.class.getField("field").getGenericType();

鑒於此,被調用函數可以提取基類型並將其用於轉換或newInstance,但虛擬字段業務肯定看起來很難看。

請注意,這樣的函數並不總是調用newInstance 顯然,如果他們這樣做,他們不需要調用resultClass.cast

您無法在標准Java API中執行此操作。 但是,有一些實用程序類可以從通用類中獲取Type 例如,Google Gson(將JSON轉換為值得信賴的Javabeans,反之亦然),就有一個TypeToken類。 您可以按如下方式使用它:

List<Person> persons = new Gson().fromJson(json, new TypeToken<List<Person>>() {}.getType());

這樣的結構正是你所需要的。 你可以在這里找到源sode,你也可以發現它很有用。 它只需要一個可以接受Type的額外getResultData()方法。

好,。 這是一個丑陋的(部分)解決方案。 你不能一般地對通用參數進行參數化,所以你不能寫

public <T<U,V>> T getResultData ...

但是你可以返回參數化集合,例如一組

public < T extends Set< U >, U > T getResultData( Class< T > resultClass, Class< U > paramClass ) throws IllegalAccessException, InstantiationException {
        return resultClass.newInstance();
    }

如果它存在於類簽名中,你可以擺脫U-param。

是的我不認為這是可能的。

想象一下這個場景。 您有一個文件,其中包含您想要讀取的序列化對象。 如果你使函數通用,你將能夠在沒有強制轉換的情況下使用它。 但是,假設您將Map序列化為文件。

當您反序列化它時, 無法知道原始通用映射類型是什么。 就java而言,它只是一個Map

我用列表遇到了這個,這很煩人。 但我實際上最終做的是創建一個通用的“轉換集合”類,它采用集合和“轉換器”(可以是匿名內部類型)。

然后,當您遍歷轉換集合時,每個項目都會被強制轉換。 這解決了警告問題。 除了警告之外,還有很多工作要做,但我不認為這是我提出這個框架的主要原因。 您還可以執行更強大的操作,例如獲取文件名集合,然后編寫一個轉換器來加載文件中的數據,或進行查詢等等。

如果要使用類型安全容器類,可以使用TypeLiteral 它們在Guice中用於提供類型安全綁定。 有關更多示例和TypeLiteral類,請參閱Guice源代碼。

順便說一句,如果你調用resultClass.newInstance() ,你不需要resultClass.newInstance()

public <T> T getResultData(Class<T> resultClass, other_args) {       
   ...
   return resultClass.newInstance();
}

所以,如果我理解你真正想要做的是(非法偽語法):

public <T, S, V> T<S, V> getResultData(Class<T<S, V>> resultClass, other_args) { 
  ...
  return resultClass.cast(T-thing);
}

你不能因為你不能進一步參數化泛型類型T 搞亂java.lang.reflect.*會有所幫助:

  1. 沒有Class<Map<String, String>>這樣的東西,因此無法動態創建其實例。
  2. 實際上沒有辦法將該方法聲明為返回T<S, V>

如果你真的不需要地圖的確切泛型類型,而你的代碼不依賴於那種類型,但你只是對你可以編寫的警告感到惱火:

Map<?, ?> returnedMap;
returnedMap = thing.getResultData(Map.class, some_other_args);

然后你可以使用任何非類型參數化的map方法,如containsKey,clear,iterator等,或迭代entrySet或將returnedMap傳遞給任何泛型方法,一切都是類型安全的。

Map<?, ?> returnedMap;
returnedMap = thing.getResultData(Map.class, some_other_args);

Object myKey = ...;

Object someValue = returnedMap.get(myKey);

for (Iterator<? extends Map.Entry<?,?>> it = returnedMap.entrySet().iterator(); it.hasNext(); )
  if (it.next().equals(myKey))
    it.remove();

for (Map.Entry<?,?> e : returnedMap.entrySet()) {
  if (e.getKey().equals(myKey))
    e.setValue(null); // if the map supports null values
}

doSomething(returnedMap);

...

<K,V> void doSomething(Map<K,V> map) { ... }

實際上,您可以使用類型安全的方式執行許多操作,而無需了解其鍵或值類型。

暫無
暫無

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

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