简体   繁体   English

Java泛型函数:如何返回泛型类型

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

Here's a Java generic pattern: 这是一个Java通用模式:

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

A typical call looks like: 典型的通话看起来像:

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

I've never been able to figure out how to use this pattern cleanly when the desired return type is, itself, generic. 当期望的返回类型本身是通用的时候,我从来没有弄清楚如何干净地使用这个模式。 To be 'concrete', what if a function like this wants to return Map<String,String> ? 要具体化,如果像这样的函数想要返回Map<String,String>怎么办? Since you can't get a class object for a generic, of course, the only option would be to pass Map.class , and then you need a cast and an @SuppressWarning after all. 因为你无法获得泛型的类对象,当然,唯一的选择是传递Map.class ,然后你需要一个@SuppressWarning和一个@SuppressWarning

One ends up with a call like: 一个人最终会打电话:

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

Now one is back to needing casts and suppressing a warning. 现在回到需要强制转换并抑制警告。

I suppose that one could take something from the java.lang.reflect.Type family instead of the Class , but those aren't especially easy to concoct. 我想可以从java.lang.reflect.Type系列而不是Class获取一些东西,但这些并不是特别容易编造的。 That looks like: 看起来像:

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

...

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

Given this, the called function could extract the base type and use that for casting or newInstance-ing, but the dummy field business sure looks ugly. 鉴于此,被调用函数可以提取基类型并将其用于转换或newInstance,但虚拟字段业务肯定看起来很难看。

Note that functions like this are not always calling newInstance . 请注意,这样的函数并不总是调用newInstance Obviously, if they do, they don't need to call resultClass.cast . 显然,如果他们这样做,他们不需要调用resultClass.cast

You cannot do that in the standard Java API. 您无法在标准Java API中执行此操作。 However, there are utility classes available which can get the Type out of a generic class. 但是,有一些实用程序类可以从通用类中获取Type In for example Google Gson (which converts JSON to fullworthy Javabeans and vice versa) there's a TypeToken class. 例如,Google Gson(将JSON转换为值得信赖的Javabeans,反之亦然),就有一个TypeToken类。 You can use it as follows: 您可以按如下方式使用它:

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

Such a construct is exactly what you need. 这样的结构正是你所需要的。 You can find here the source sode, you may find it useful as well. 你可以在这里找到源sode,你也可以发现它很有用。 It only requires an additional getResultData() method which can accept a Type . 它只需要一个可以接受Type的额外getResultData()方法。

Well,. 好,。 here's an ugly (partial) solution. 这是一个丑陋的(部分)解决方案。 You can't generically paramaterize a generic param, so you can't write 你不能一般地对通用参数进行参数化,所以你不能写

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

but you can return a parameterized collection, eg for a set 但是你可以返回参数化集合,例如一组

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

where you could get rid of the U-param if it existed in the class signature. 如果它存在于类签名中,你可以摆脱U-param。

Yeah I don't think this is possible. 是的我不认为这是可能的。

Imagine this scenario. 想象一下这个场景。 You have a file with a serialized object that you want to read it. 您有一个文件,其中包含您想要读取的序列化对象。 If you make the function generic, you'll be able to use it without a cast. 如果你使函数通用,你将能够在没有强制转换的情况下使用它。 But lets say you have a Map serialized into a file. 但是,假设您将Map序列化为文件。

When you deserialize it, there's no way to know what the original generic map type was. 当您反序列化它时, 无法知道原始通用映射类型是什么。 As far as java is concerned, it's just a Map 就java而言,它只是一个Map

I ran into this with lists, and it was annoying. 我用列表遇到了这个,这很烦人。 But what I actually ended up doing was creating a generic "converting collection" class that takes a collection and a "converter" (which can be an anonymous inner type). 但我实际上最终做的是创建一个通用的“转换集合”类,它采用集合和“转换器”(可以是匿名内部类型)。

Then when you iterate through the converting collection, every item gets cast. 然后,当您遍历转换集合时,每个项目都会被强制转换。 And that solved the warning problem. 这解决了警告问题。 It's a lot of work just to get rid of the warning, but I don't think that was the main reason I came up with that framework. 除了警告之外,还有很多工作要做,但我不认为这是我提出这个框架的主要原因。 You can also do more powerful things like take a collection of filenames, and then write a converter that loads the data in the file, or does a query, or whatever. 您还可以执行更强大的操作,例如获取文件名集合,然后编写一个转换器来加载文件中的数据,或进行查询等等。

You can use TypeLiteral if you want to use typesafe container classes. 如果要使用类型安全容器类,可以使用TypeLiteral They are used in Guice to provide typesafe bindings. 它们在Guice中用于提供类型安全绑定。 See Guice Source code for more examples and TypeLiteral class. 有关更多示例和TypeLiteral类,请参阅Guice源代码。

By the way, ou don't need cast, if you call resultClass.newInstance() for instance. 顺便说一句,如果你调用resultClass.newInstance() ,你不需要resultClass.newInstance()

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

So if I understood correctly what you really trying to do is (illegal pseudo-syntax): 所以,如果我理解你真正想要做的是(非法伪语法):

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

You can't because you can't further parameterize generic type T . 你不能因为你不能进一步参数化泛型类型T Messing about with java.lang.reflect.* won't help: 搞乱java.lang.reflect.*会有所帮助:

  1. There is no such thing as Class<Map<String, String>> and thus no way to dynamically create its instance. 没有Class<Map<String, String>>这样的东西,因此无法动态创建其实例。
  2. There is no way to actually declare the method in question as returning T<S, V> 实际上没有办法将该方法声明为返回T<S, V>

If you don't really need the exact generic type of the map and your code does not rely on that type but you're simply annoyed by the warnings you could write: 如果你真的不需要地图的确切泛型类型,而你的代码不依赖于那种类型,但你只是对你可以编写的警告感到恼火:

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

then you can use any of the map methods that are not type-parametrized like containsKey, clear, iterator and so on or iterate over the entrySet or pass returnedMap to any generic method and everything will be type safe. 然后你可以使用任何非类型参数化的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) { ... }

there is actually many things that you can do with a Map in a type-safe manner without knowing its key or value type. 实际上,您可以使用类型安全的方式执行许多操作,而无需了解其键或值类型。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM