Javagenerics函数:如何返回generics类型

这是一个Java通用模式:

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

典型的通话看起来像:

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

当期望的返回类型本身是通用的时候,我从来没有弄清楚如何干净地使用这个模式。 要具体化,如果像这样的函数想要返回Map怎么办? 因为你不能获得generics的类对象,当然,唯一的选择是传递Map.class ,然后你需要一个@SuppressWarning和一个@SuppressWarning

一个人最终会打电话:

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

现在回到需要强制转换并抑制警告。

我想可以从java.lang.reflect.Type系列而不是Class获取一些东西,但这些并不是特别容易编造的。 看起来像:

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

鉴于此,被调用函数可以提取基类型并将其用于转换或newInstance,但虚拟字段业务肯定看起来很难看。

请注意,这样的函数并不总是调用newInstance 。 显然,如果他们这样做,他们不需要调用resultClass.cast

您无法在标准Java API中执行此操作。 但是,有一些实用程序类可以从通用类中获取Type 。 例如,Google Gson(将JSON转换为值得信赖的Javabeans,反之亦然)就是一个TypeToken类。 您可以按如下方式使用它:

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

这样的结构正是你所需要的。 你可以在这里找到源sode,你也可以发现它很有用。 它只需要一个可以接受Type的额外getResultData()方法。

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

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

你不能因为你不能进一步参数化generics类型T 搞乱java.lang.reflect.*将无济于事:

  1. 没有Class>这样的东西,因此无法动态创建其实例。
  2. 实际上没有办法将有问题的方法声明为返回T

好,。 这是一个丑陋的(部分)解决方案。 你不能一般地对通用参数进行参数化,所以你不能写

 public > T getResultData ... 

但是你可以返回参数化集合,例如一组

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

如果它存在于类签名中,你可以摆脱U-param。

如果要使用类型安全容器类,可以使用TypeLiteral 。 它们在Guice中用于提供类型安全绑定。 有关更多示例和TypeLiteral类,请参阅Guice源代码。

顺便说一句,如果你调用resultClass.newInstance() ,你不需要resultClass.newInstance()

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

是的我不认为这是可能的。

想象一下这种情况。 您有一个文件,其中包含您想要读取的序列化对象。 如果你使函数通用,你将能够在没有强制转换的情况下使用它。 但是,假设您将Map序列化为文件。

反序列化时, 无法知道原始通用映射类型是什么。 就java而言,它只是一个地图

我用列表遇到了这个,这很烦人。 但实际上我最终做的是创建一个通用的“转换集合”类,它带有一个集合和一个“转换器”(可以是一个匿名的内部类型)。

然后,当您遍历转换集合时,每个项目都会被转换。 这解决了警告问题。 除了警告之外,还有很多工作要做,但我不认为这是我提出这个框架的主要原因。 您还可以执行更强大的操作,例如获取文件名集合,然后编写一个转换器,将数据加载到文件中,或者执行查询等等。

如果你真的不需要地图的确切generics类型,而你的代码不依赖于那种类型,但你只是被你可以编写的警告所困扰:

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

然后你可以使用任何非类型参数化的map方法,如containsKey,clear,iterator等,或者遍历entrySet或将returnedMap传递给任何generics方法,一切都是类型安全的。

 Map returnedMap; returnedMap = thing.getResultData(Map.class, some_other_args); Object myKey = ...; Object someValue = returnedMap.get(myKey); for (Iterator> 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); ...  void doSomething(Map map) { ... } 

实际上,您可以使用类型安全的方式执行许多操作,而无需了解其键或值类型。