Reputation: 1444
I'm using GSON to parse json in Java. When I want to get an integer list from a json String, my code may look like this:
Type type = new TypeToken<List<Integer>>(){}.getType();
List<Integer> result = null;
try {
result = new Gson().fromJson(jsonStringGoesHere, type);
} catch (JsonSyntaxException e) {
e.printStackTrace();
}
If jsonStringGoesHere
is an empty String or null, result
is also null.
It is considered bad practice to have an Collection equal null though, so I would prefer to have an empty List instead of null for this case.
So basically this is what I want to do now: If result
is null, I want to check if type
is a List
(or a subtype of List
). If true, I'll make result an empty ArrayList. How do I check whether type
contains a List
or subtype of List
though?
My attempt was checking if(type instanceof Collection)
- but that returns false.
My next try was to take type.getTypeName()
which returns the String java.util.List<java.lang.Integer>
, and tried to create an object from that:
Class c = Class.forName(type.getTypeName());
Object o = c.newInstance();
if(o instanceof List){
System.out.println("it's working!");
}
But this throws java.lang.ClassNotFoundException: java.util.List<java.lang.Integer>
.
Any ideas how to make this work?
Upvotes: 2
Views: 1814
Reputation: 21125
My attempt was checking
if(type instanceof Collection)
- but that returnsfalse
.
This won't work because Type
and Collection
are not "mutually castable" and exist in different hierarchies.
My next try was to take
type.getTypeName()
which returns the Stringjava.util.List<java.lang.Integer>
This won't work either. Class.forName
returns an existing Class<?>
instance by a fully qualified name (simply speaking, something that's represented by a .class
file in CLASSPATH
), but never -- a parameterized class (classes are not parameterizable but can be subclasses with parameterized superclasses -- this is what's under the hood of type tokens). Thus, Class.forName("java.util.List")
makes sense, but Class.forName("java.util.List<String>")
does not.
Note that Type
is something that can represent a type, and TypeToken<T>
serves this purpose. Just take a look at how it's declared -- and you'll understand it better. Even more, its subinterface ParameterizedType
can hold type parameters, and this this can be generated either by TypeToken
or constructed manually (as usual -- just take a look at the interface declaration). TypeToken
s usually use anonymous base class parameterization to take the type parameters from, and that's why they are designed like that: well-done compile-time support + there are similar techniques in Google Guava, Jackson, Spring Framework (but it would be nice if Java could support something like java.util.List<Integer>.type
to get a parameterized type instance). As of later Gson versions, you can even use TypeToken.getParameterized(...)
that is simply a convenience method to create a parameterized type.
What you're looking for is, a "class-instanceof" or "a-subclass-of" (if it's fine to call it like that): Class.isAssignableFrom(...)
. This instance method checks if one class is the same or a subclass of a another class. Say,
SuperClass.class.isAssignableFrom(SubClass.class)
always returns true
. So you only have to check like this:
final Type type = new TypeToken<List<Integer>>() {
}.getType();
final TypeToken<?> typeToken = TypeToken.get(type);
System.out.println(List.class.isAssignableFrom(typeToken.getRawType()));
Or even shorter:
final TypeToken<List<Integer>> typeToken = new TypeToken<List<Integer>>() {
};
System.out.println(List.class.isAssignableFrom(typeToken.getRawType()));
Both return true
. Note that type tokens return Class<?>
instances via getRawType()
, but not getType()
since the latter may represent not a class (say, what would Collection<String>.class.isAssignableFrom(List<Integer.class>)
return?)
Also, you can extract type tokens and its types to static final
fields since they are meant to be immutable values and should not change in runtime.
Upvotes: 1
Reputation: 2181
In Java Generic part of collection comes into play at compile time, and therefore it is not available at runtime and that is the reason you are getting that exception. So if you want to create class of Collection type using reflection then you have to remove the generic part.
Also List,Set and Map are interfaces they can not be instantiated directly. So in order to create an object you have to use one of their implementation, like ArrayList,LinkedList etc.
Upvotes: 0