且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

在Java中为类属方法传递类型参数为类型参数的类

更新时间:2022-10-16 17:24:18

p>这实际上可能在Java,使用一些技巧。不要屈服于C#***者的压力! (j / k)



技巧是创建一个扩展泛型类型的类,并通过 c>或类型返回 .getGenericSuperclass()



这很麻烦。为了简化我们的生活,Google已经为我们编写了大部分无聊的代码部分,并通过Guava提供。



检查 TypeToken 类,你要什么。例如:

  TypeToken< List< String>> stringListTok = new TypeToken< List< String>>(){}; 

然后传递 TypeToken< T> 而不是类< T> ,这就是全部。它提供了对T表示的类型做反射的方法。



这里做的是简单地调用 .getClass()。getGenericSuperclass ()(或 ... Interfaces()),然后一些丑陋的转换从类型 ParameterizedType 并从中检索所有信息( .getActualTypeArguments()等)。



最后,如果你想使用依赖注入做一些类似的事情(例如,假设你需要注入一个 Class< T> 类的构造函数,或者想要获得一些参数化接口的实例,其中实例应该依赖于类型参数),Google Guice(来自Google的DI容器)有一个非常类似的机制来解决这个问题,称为 TypeLiteral 。场景的使用和代码几乎与来自Guava的 TypeToken 完全相同。请点击此处: TypeLiteral


Problem summary: I would like to pass a class with a type parameter (such as ArrayList<SomeClass>, for example) to a generic method as a type parameter.

Let's say I have a method:

    public static <T> T getGenericObjectFromJson(String json, Class<T> genericType){
        // details unimportant, basically returns an object of specified type
        return JsonParser.fromJson(json, genericType); 
    }

This method, of course, will work perfectly fine for any kind of class. I can call the method like so, for example:

getGenericObjectFromJson(jsonString, User.class)

The problem: I discovered I cannot do this:

getGenericObjectFromJson(jsonString, ArrayList<User>.class)

Syntactically, this is obviously invalid. However, I am not certain how I would even accomplish something like this. I can, of course, pass ArrayList.class, however the addition of the generic type makes it no longer syntactically valid, and I cannot think of a way around it.

The only direct solution has been something like this (which seems rather goofy):

getGenericObjectFromJson(jsonString, new ArrayList<User>().class)

However we end up losing the generic type anyways, and merely get back an ArrayList of unknown type (though it can be cast). Plus, unnecessarily instantiating an object.

My only solution thus far has been to wrap that method in a class that contains a generic type parameter which can be instantiated, like so:

public class JsonDeserializer<T>...

In this case, the getGenericObjectFromJson method will use the class's generic type.

The Question(s): Ultimately, I am curious why I cannot pass a class with a type parameter, AND whether there is a way to accomplish what I attempted to do.

As always, let me know if there are any problems with this question.

This is actually possible in Java, using some "tricks". Don't succumb to pressure from the C# fanatics! (j/k)

The "trick" is to create a class that extends a generic type, and access the value of the type parameter of the parent class through the Type returned by .getGenericSuperclass() or .getGenericInterfaces().

This is quite cumbersome. To simplify our lives, Google has already written most of the boring part of the code for us, and made it available through Guava.

Check the TypeToken class, which does exactly what you want. For example:

TypeToken<List<String>> stringListTok = new TypeToken<List<String>>() {};

Then you pass around a TypeToken<T> instead of a Class<T> and that's all. It provides you with methods to do reflection on the type represented by T.

What this is doing internally is simply calling .getClass().getGenericSuperclass() (or ...Interfaces()), then some ugly casts from Type to ParameterizedType and retrieving all the information from there (.getActualTypeArguments(), etc).

Finally, if you want to do something similar with Dependency Injection (ie, suppose you need to inject a Class<T> on the constructor of a class, or you want to get an instance of some parameterized interface, in which the instance should depend on the type parameter), Google Guice (a DI container from Google) has a very similar mechanism to solve the problem, called TypeLiteral. The use and the code behind the scenes are almost identical to TypeToken from Guava. Check it here: TypeLiteral