Obtain Class from Jackson TypeReference - java

I have some code which needs to unpick a Jackson TypeReference to find out if it is a Collection. At the moment the best I can come up with is:
// Sample type reference - in reality this is an argument to the method
final TypeReference<List<String>> typeRef = new TypeReference<List<String>>(){};
// Obtain the Java reflection type from the TypeReference
final Type type = typeRef.getType() instanceof ParameterizedType ? ((ParameterizedType)typeRef.getType()).getRawType() : typeRef.getType();
// Obtain the name of the class (or interface)
final String typeName = type.toString().replace("class ", "").replace("interface ", "");
// And find out if it is a Collection
final boolean isCollection = Collection.class.isAssignableFrom(Class.forName(typeName));
But I would hope that there is a way to do this without string manipulation. Is there a better way to go from the Java Type to the Class, or indeed to check assignability directly from either the TypeReference or the Type?
This needs to work on Android so any features added in Java 8 can't be used.

Based on your line of code,
final Type type = typeRef.getType() instanceof ParameterizedType ? ((ParameterizedType)typeRef.getType()).getRawType() : typeRef.getType();
You can safely cast it to a Class like this
final Class clazz = (Class)(typeRef.getType() instanceof ParameterizedType ? ((ParameterizedType)typeRef.getType()).getRawType() : typeRef.getType());
To add a little more explanation -
In the first scenario where ( typeRef is an instance of ParameterizedType), you are retrieving the rawType which would be a Class.
In the second scenario where (typeRef is not an instance of ParameterizedType), it would still be a regular Class because it is not Parameterized.

Related

TypeReference<Map<String, String>>() { }

Since few days ago I started to work on a webservice project. This project is using Jackson to marshalling and unmarshalling JSON objects. So my question is:
Why always I have to put the {} when I am creating an instance of TypeReference? I know the constructor is protected, but why is protected? I think that it's like a hack to make visible the constructor creating an implementation of the constructor since TypeReference is abstract and you can do it. But what is the point of this?
String jsonString = "{\" firstName\":\"John\",\"lastName\":\"Chen\"}";
ObjectMapper objectMapper = new ObjectMapper();
// properties will store name and value pairs read from jsonString
Map<String, String> properties = objectMapper.readvalue(
jsonString, new TypeReference<Map<String, String>>()
{ //
});
TL;DR
Via subclassing it is possible for TypeReference to extract the actual generic type parameter. E.g:
TypeReference<String> ref = new TypeReference<String>(){};
System.out.println(ref.getType());
Prints:
class java.lang.String
This can be useful when you can't use normal classes. E.g when this doesn't work:
// doesn't work
Type type = ArrayList<String>.class;
You still can get that class by using a TypeReference:
// will yield Class<ArrayList<String>>>
Type type = new TypeReference<ArrayList<String>>(){}.getType();
Detailed
When looking at the source code of TypeReference (using Jackson 2.8.5) you can see that the constructor body contains the following lines:
Type superClass = getClass().getGenericSuperclass();
if (superClass instanceof Class<?>) { // sanity check, should never happen
throw new IllegalArgumentException("Internal error: TypeReference constructed without actual type information");
}
_type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
The interesting lines are the first and last. Let's take a closer look at the first line:
Type superClass = getClass().getGenericSuperclass();
For example when you're creating a subclass, by using an anonymous class:
TypeReference<SomeStype> ref = new TypeReference<SomeType>(){};
Then getClass returns the current Class object (an anonymous class), and getGenericSuperclass() will return the Class object from the class the current implementation extends from, in our case, superClass will equal Class<TypeReference<?>>.
Now when looking at the last line from the constructor body:
_type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
As we know that the superClass is the Class object for TypeReference<?> we know that it has a generic parameter. Hence the cast to ParameterizedType. This specified Type has the method getActualyTypeArguments() which returns an array of all generic parameters specified by that class. In our case it's just 1. So [0] will yield the first element. In the example we will get the actually specified type parameter SomeType.

Specifying generics for a class type

I have a method which essentially handles casting for config types, however upon specifying a generic type (such as List), it becomes a problem of how to handle the specific type. In an ideal world, something such as using a type witness:
List<String> someVal = MyConfig.SOME_VAL.<List<String>>.as(List.class);'
(The full as code):
/**
* Attempts to return the {#link Config} value as a casted type. If the
* value cannot be casted it will attempt to return the default value. If
* the default value is inappropriate for the class, the method will
* throw a {#link ClassCastException}.
*
* #since 0.1.0
* #version 0.1.0
*
* #param <T> The type of the casting class
* #param c The class type to cast to
* #return A casted value, or {#code null} if unable to cast. If the passed
* class parameter is of a primitive type or autoboxed primitive,
* then a casted value of -1 is returned, or {#code false} for
* booleans. If the passed class parameter is for {#link String},
* then {#link Object#toString()} is called on the value instead
*/
default public <T> T as(Class<T> c) {
Validate.notNull(c, "Cannot cast to null");
Validate.isTrue(Primitives.unwrap(c) != void.class, "Cannot cast to a void type");
Object o = this.get();
if (o == null) {
T back = Reflections.defaultPrimitiveValue(c);
if (back != null) { //catch for non-primitive classes
return back;
}
}
if (c == String.class) {
return (T) String.valueOf(o);
}
if (c.isInstance(o)) {
return c.cast(o);
}
if (c.isInstance(this.getDefault())) {
return c.cast(this.getDefault());
}
throw new ClassCastException("Unable to cast config value");
}
So essentially that leaves me with a two-part question: Why can't type witnesses be used for generics on a class (such as List(raw) -> List<String>), and how can I go about supporting retrieving a class with generic bounding without doing extraneous casting? The first point particularly baffles me, since this is perfectly legal:
List<String> test = new ArrayList<>();
test = MyConfig.FRIENDLY_MOBS.as(test.getClass());
Despite it returning a raw-typed list
That line is really evil (type erasure / raw type) as there is no check whatsoever whether the Collection type really contains strings.
test = MyConfig.FRIENDLY_MOBS.as(test.getClass());
I think the easiest solution is to write an as method that takes the class object of both the collection type as well as the element class. See this following example (in static scope, so you have to adjust it):
static List<String> words = Arrays.asList("Hello", "Bonjour", "engine");
static public <E, Coll extends Collection<E>> Coll as(Class<? extends Collection> collClass, Class<E> elemClass) {
if (collClass.isInstance(words)) {
Collection coll = collClass.cast(words);
for (Object o : coll)
if (!elemClass.isInstance(o))
throw new ClassCastException("BAM");
return (Coll)coll;
}
return null;
}
Now the following behaviour is found:
final List<String> list = as(List.class, String.class); // works
final List<String> list = as(List.class, Integer.class); // compile error: type check
final List<Integer> list = as(List.class, String.class); // compile error: type check
final List<Integer> list = as(List.class, Integer.class); // ClassCastException
As for other attempts: iirc Jackson had some magic TypeToken stuff going on that allowed to capture types such as List<String>. It somehow abused Enum<T> I think...
Your idea of type witnesses is indeed the way to go, but you need a better type witness that does not only capture the raw type (here List) but also its generic parameters. This is not easy in Java because in most places the generic parameters are not available at runtime due to type erasure. Java's reflection API uses interfaces that are subinterfaces of Type as a runtime representation of generic types, but these are unsuited for your purpose because they do not provide any compile-time type information.
However, with a trick it is possible to achieve what you want.
The trick is based on the fact that if a class (example: MyClass) inherits from a generic type (example: List<String>), there is no type erasure. You can at runtime retrieve the information that MyClass inherits from List<String> (using the method Class.getGenericSuperclass()).
Subclassing the actual type we want to pass would however be very inflexible (for example this would not work for final classes). Thus we create a special class (often called TypeToken) from which we can inherit. The TypeToken class has a generic parameter, and in the subclass we specify the type we want to pass as this parameter. Of course creating a special class for every different value you want to pass would usually be quite cumbersome, but fortunately we can use anonymous classes to make this easy.
Putting it all together, a solution could look like the following.
Definition of our type token:
public abstract class TypeToken<T> {}
Definition of method as:
public <T> T as(TypeToken<T> typeToken) {
Type targetType = typeToken.getClass().getGenericSuperclass();
// use targetType here, e.g.
if (targetType instanceof ParameterizedType) { ... }
And calling it:
List<Integer> list = MyConfig.SOME_VAL.as(new TypeToken<List<String>>() {});
Notice the {} that declares the anonymous subclass and avoids the type erasure.
Even better would be to use an existing class as the type token, for example the class TypeToken of the great Guava library (if you do not know this library yet, also look at what else it offers and consider using it!). This class also provides additional helper methods that make it easer to use the token in the as method (directly using Type instances can be difficult). The Guava wiki has more information on its TypeToken class.
If you are worried about creating too many classes, you can of course easily provide a few default instances for common cases like TypeToken<List<String>> etc. Guava's TypeToken also has an of(Class<T>) method that can be used for non-generic types, so the subclasses would be restricted to cases where it is actually necesary.
Other projects also use this trick, for example Guice with class TypeLiteral (explanation), Gson (TypeToken), and Jackson (TypeReference). So I would not worry too much about the amount of subclasses, given that they do not clutter your source code.

How do you get List<Type> with reflection?

I have am getting a method, and checking method.getParameterTypes()[0], which is a java.util.List. But I want to figure out what the containing type is, for instance if it's a java.util.List<String> I want to figure out that it should hold strings.
How do I do this?
Thanks!
Type listType = method.getGenericParameterTypes()[0];
if (listType instanceof ParameterizedType) {
Type elementType = ((ParameterizedType) listType).getActualTypeArguments()[0];
}
Note that the element type needn't be an actual Class like String -- it could be a type variable, a wildcarded type, etc.
You can read more about scraping generics info from reflected items here and here.
It's simple : you can't. At compile time, Java erases generic types (see Type erasure).
You can see here and here for more information about GetGenericType (which I didn't know about, honestly), but it seems rather limited.
I had the same issue, and I did as below
in here : your class is initialize as clazz (eg - Company.Class) and your Collection as
List<Employees> companyEmployee
String collectionType- List
String collectionContent - Employee
Method[] methodList = clazz.getDeclaredMethods();
Method classMethod = methodList[0];
String[] collection= classMethod.getReturnType().toString().split("\\.");
String collectionType = collection[collection.length - 1]
String collectionContent = classMethod.getGenericReturnType().getTypeName().split("<")[1].split(">")[0]

Instantiating type with generics dynamically

I'm guessing there is absolutely no way I can do something like:
Class c = Class.forName("Processor<Integer, String>");
in Java? (where I defined Processor previously, of course).
Absolutely no way as generic arguments can exist only at compile time. Class object is the same on runtime. It is not class template as in C++. Type parameters are just information for java compiler.
You can try to accomplish something similar by creating a class :
class IntStringProcessor extends Processor<Integer, String> {}
and Class c = Class.forName("IntStringProcessor");
On runtime you can get actual Processor type parameters via c.getGenericSuperclass() but this extends the question I guess.
EDIT: Another idea
Your Processor may keep classess as parameters and then:
Class c1 = Class.forName("java.lang.Integer");
Class c2 = Class.forName("java.lang.String");
Processor = new Processor(c1,c2);
Your processor is now not generic but uses Class instances inside. As I said, there is no way to create generic instance on runtime because generics exist only at compile time.
First let me straighten something. There is no Class object in Java for Processor<Integer, String>. There is only one Class object for Processor, namely Processor.class and all generic instantiations share the same Class object:
Processor<Integer, String> x = new Processor<Integer, String>();
Processor<Character, Byte> y = new Processor<Character, Byte>();
System.out.println(x.getClass() == y.getClass()); // prints true
Having said that, if all you need is to have tokens for generic types to pass around, you can use a third party implementation. Here I use Java ClassMate:
TypeResolver typeResolver = new TypeResolver();
// type token for Processor<Integer, String>
ResolvedType type = typeResolver.resolve(Processor.class, Integer.class, String.class);
System.out.println("type = " + type);
If you read names of Java types as String's from a file, you probably want to use Class.forName() instead of my .class literals.
You'll have to parse <, > brackets yourself, but that should be easy, as those single characters always mean the same thing in Java type names...
You can form nested parameters like this:
// type token for Processor<Processor<Integer, String>, Character>
ResolvedType type = typeResolver.resolve(Processor.class,
typeResolver.resolve(Processor.class, Integer.class, String.class),
typeResolver.resolve(Character.class)
);
System.out.println("type = " + type);
If you want to create type tokens for types you know at compilation time, you can use the so-called super-type-token pattern. Just put any type name you need inside new GenericType<...>() {}:
type = typeResolver.resolve(new GenericType<Processor<Processor<Integer, String>, Character>>() {});
System.out.println("type = " + type);

Java: List<E> know E.getClass()

As we all know the Generics about Java Collections that E or a wildcard ? is required to instantiate the allowed contents / objects in a particular collection.
My question is there a way we could know the wildcard or object of a particular collection from the code below?
Object inbound = java.io.ObjectInputStream().readObject();
if(inbound instanceof List<?>) {
// know `?.getClass()`
}
No. Due to type erasure, during runtime there is no way to differentiate between Lists.
You can only find the type parameters if inbound is a class that defines its type parameters in a type declaration. For example, suppose you serialized an instance of NodeList that was declared like this:
final class NodeList extends ArrayList<Node> { }
Then when you deserialize it, you can do this:
Object inbound = ois.readObject();
if (inbound instanceof List<?>) {
Type t = inbound.getClass().getGenericSuperclass();
if (t instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) t;
for (Type p : pt.getActualTypeArguments()) {
if (p instanceof TypeVariable<?>)
System.out.println("Unknown");
else
System.out.println(p); /* Prints "interface Node" */
}
}
}
You could if you would be certain that the list contains at least one element, in which case you could simply call getClass on that.
Otherwise this wouldn't really be possible, although you could serialize the correct Class object along with the List.
IMHO, the best solution if you need to do this is with Guice: http://blog.publicobject.com/2008/11/guice-punches-erasure-in-face.html.
As others have mentioned, type erasure is the source of the problems. A good explanation is at http://docs.oracle.com/javase/tutorial/java/generics/erasure.html.
You can take a look at the source for ArrayList and see an example of how this works. The ArrayList is holding an Object[] (line 111). It doesn't know the types of the objects. You can see that the 'type safety' is actually just achieved via casts, e.g. line 371.

Categories