Java泛型函数:如何返回泛型类型
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1959022/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Java generic function: how to return Generic type
提问by bmargulies
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>
? 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<String,String>
怎么办?由于您无法获得泛型的类对象,当然,唯一的选择是通过Map.class
,然后您需要一个演员表和一个@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. That looks like:
我想一个人可以从java.lang.reflect.Type
家人那里拿一些东西而不是从Class
,但那些不是特别容易炮制。看起来像:
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-ing,但虚拟字段业务确实看起来很丑陋。
Note that functions like this are not always calling newInstance
. Obviously, if they do, they don't need to call resultClass.cast
.
请注意,像这样的函数并不总是调用newInstance
. 显然,如果他们这样做,他们不需要调用resultClass.cast
.
采纳答案by BalusC
You cannot do that in the standard Java API. However, there are utility classes available which can get the Type
out of a generic class. In for example Google Gson (which converts JSON to fullworthy Javabeans and vice versa) there's a TypeToken
class. You can use it as follows:
您不能在标准 Java API 中做到这一点。但是,有可用的实用程序类可以摆脱Type
泛型类。例如,在 Google Gson(将 JSON 转换为完整的 Javabeans,反之亦然)中有一个TypeToken
类。您可以按如下方式使用它:
List<Person> persons = new Gson().fromJson(json, new TypeToken<List<Person>>() {}.getType());
Such a construct is exactly what you need. You can find herethe source sode, you may find it useful as well. It only requires an additional getResultData()
method which can accept a Type
.
这样的构造正是您所需要的。你可以在这里找到源代码,你可能会发现它也很有用。它只需要一个getResultData()
可以接受Type
.
回答by Chandra Patni
You can use TypeLiteralif you want to use typesafe container classes. They are used in Guiceto provide typesafe bindings. See Guice Source code for more examples and TypeLiteralclass.
如果要使用类型安全的容器类,可以使用TypeLiteral。它们在Guice 中用于提供类型安全绑定。有关更多示例和TypeLiteral类,请参阅 Guice 源代码。
By the way, ou don't need cast, if you call resultClass.newInstance()
for instance.
顺便说一句,如果你打电话,你不需要演员表resultClass.newInstance()
。
public <T> T getResultData(Class<T> resultClass, other_args) {
...
return resultClass.newInstance();
}
回答by ChssPly76
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
. Messing about with java.lang.reflect.*
won't help:
你不能,因为你不能进一步参数化泛型类型T
。胡闹java.lang.reflect.*
无济于事:
- There is no such thing as
Class<Map<String, String>>
and thus no way to dynamically create its instance. - There is no way to actually declarethe method in question as returning
T<S, V>
- 没有这样的事情
Class<Map<String, String>>
,因此无法动态创建其实例。 - 无法将相关方法实际声明为返回
T<S, V>
回答by Steve B.
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, e.g. 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 存在于类签名中,您可以在其中删除它。
回答by Chad Okere
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 knowwhat 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.
然后,当您遍历转换集合时,每个项目都会被转换。这解决了警告问题。为了消除警告需要做很多工作,但我认为这不是我提出该框架的主要原因。你还可以做更强大的事情,比如获取文件名的集合,然后编写一个转换器来加载文件中的数据,或者进行查询,或者其他什么。
回答by ciamej
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.
那么你可以使用任何没有类型参数化的映射方法,如 containsKey、clear、iterator 等,或者迭代 entrySet 或将返回的Map传递给任何泛型方法,一切都将是类型安全的。
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.
实际上,在不知道 Map 的键或值类型的情况下,您可以以类型安全的方式使用 Map 做很多事情。