ChatGPT解决这个技术问题 Extra ChatGPT

Passing a class with type parameter as type parameter for generic method in Java

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>().getClass())

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.

Due to type erasure, I don't think this is possible. Java generics are a compile-time construct and don't exist at runtime, so there is no such thing as a Class object representing a constructed generic type.
Solution: C#, nah just kidding. you can't do that due to "type erasure", which is what java does when you compile, by removing the generic type information
Would it work to recast this as getGenericArrayListFromJson(jsonString, User.class)? This is probably the best you're going to be able to do.
Actually, this is possible in Java. The generic type information is not completely lost in runtime -- you can access generic type information on the parent class or interfaces! Check my answer below.
@asgs Its been ages, but yes that's likely what I meant. Will update to avoid confusing anyone.

N
Nikunj Undhad

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


Wow, that's really awesome. So funny thing, I am actually using google Gson's library, which uses (or at least contains parts of) Guava, however I obviously did not fully understand how the TypeToken worked. Thank you very much, extremely helpful information.
Can someone elaborate more on first way. AFAIK, you cannot extend a generic type in java.
@Kedarnath, class A<T>{} class B extends A<Integer>{} --> this is absolutely fine. Is this what you are talking about? Would you be able to provide an example of what you claim to not be possible?
For anyone who has no guava dependencies, but have jackson, com.fasterxml.jackson.core.type.TypeReference is analogue.