1. Overview

In this article, we’ll be looking at the Guava reflection API – which is definitely more versatile compared to the standard Java reflection API.

We’ll be using Guava to capture generic types at runtime, and we’ll making good use of Invokable as well.

2. Capturing Generic Type at Runtime

In Java, generics are implemented with type erasure. That means that the generic type information is only available at compile time and, at runtime – it’s no longer available.

For example, List, the information about generic type gets erased at runtime. Due to that fact, it is not safe to pass around generic Class objects at runtime.

We might end up assigning two lists that have different generic types to the same reference, which is clearly not a good idea:

List<String> stringList = Lists.newArrayList();
List<Integer> intList = Lists.newArrayList();

boolean result = stringList.getClass()
  .isAssignableFrom(intList.getClass());

assertTrue(result);

Because of type erasure, the method isAssignableFrom() can not know the actual generic type of the lists. It basically compares two types that are just a List without any information about the actual type.

By using the standard Java reflection API we can detect the generic types of methods and classes. If we have a method that returns a List, we can use reflection to obtain the return type of that method – a ParameterizedType representing List.

The TypeToken class uses this workaround to allow the manipulation of generic types. We can use the TypeToken class to capture an actual type of generic list and check if they really can be referenced by the same reference:

TypeToken<List<String>> stringListToken
  = new TypeToken<List<String>>() {};
TypeToken<List<Integer>> integerListToken
  = new TypeToken<List<Integer>>() {};
TypeToken<List<? extends Number>> numberTypeToken
  = new TypeToken<List<? extends Number>>() {};

assertFalse(stringListToken.isSubtypeOf(integerListToken));
assertFalse(numberTypeToken.isSubtypeOf(integerListToken));
assertTrue(integerListToken.isSubtypeOf(numberTypeToken));

Only the integerListToken can be assigned to a reference of type nubmerTypeToken because an Integer class extends a Number class*.*

3. Capturing Complex Types Using TypeToken

Let’s say that we want to create a generic parameterized class, and we want to have information about a generic type at runtime. We can create a class that has a TypeToken as a field to capture that information:

abstract class ParametrizedClass<T> {
    TypeToken<T> type = new TypeToken<T>(getClass()) {};
}

Then, when creating an instance of that class, the generic type will be available at runtime:

ParametrizedClass<String> parametrizedClass = new ParametrizedClass<String>() {};

assertEquals(parametrizedClass.type, TypeToken.of(String.class));

We can also create a TypeToken of a complex type that has more than one generic type, and retrieve information about each of those types at runtime:

TypeToken<Function<Integer, String>> funToken
  = new TypeToken<Function<Integer, String>>() {};

TypeToken<?> funResultToken = funToken
  .resolveType(Function.class.getTypeParameters()[1]);

assertEquals(funResultToken, TypeToken.of(String.class));

We get an actual return type for Function, that is a String. We can even get a type of the entry in the map:

TypeToken<Map<String, Integer>> mapToken
  = new TypeToken<Map<String, Integer>>() {};

TypeToken<?> entrySetToken = mapToken
  .resolveType(Map.class.getMethod("entrySet")
  .getGenericReturnType());

assertEquals(
  entrySetToken,
  new TypeToken<Set<Map.Entry<String, Integer>>>() {});

Here we use a reflection method getMethod() from Java standard library to capture the return type of a method.

4. Invokable

The Invokable is a fluent wrapper of java.lang.reflect.Method and java.lang.reflect.Constructor. It provides a simpler API on top of a standard Java reflection API. Let’s say that we have a class that has two public methods and one of them is final:

class CustomClass {
    public void somePublicMethod() {}

    public final void notOverridablePublicMethod() {}
}

Now let’s examine the somePublicMethod() using Guava API and Java standard reflection API:

Method method = CustomClass.class.getMethod("somePublicMethod");
Invokable<CustomClass, ?> invokable 
  = new TypeToken<CustomClass>() {}
  .method(method);

boolean isPublicStandradJava = Modifier.isPublic(method.getModifiers());
boolean isPublicGuava = invokable.isPublic();

assertTrue(isPublicStandradJava);
assertTrue(isPublicGuava);

There is not much difference between these two variants, but checking if a method is overridable is a really non-trivial task in Java. Fortunately, the isOverridable() method from the Invokable class makes it easier:

Method method = CustomClass.class.getMethod("notOverridablePublicMethod");
Invokable<CustomClass, ?> invokable
 = new TypeToken<CustomClass>() {}.method(method);

boolean isOverridableStandardJava = (!(Modifier.isFinal(method.getModifiers()) 
  || Modifier.isPrivate(method.getModifiers())
  || Modifier.isStatic(method.getModifiers())
  || Modifier.isFinal(method.getDeclaringClass().getModifiers())));
boolean isOverridableFinalGauava = invokable.isOverridable();

assertFalse(isOverridableStandardJava);
assertFalse(isOverridableFinalGauava);

We see that even such a simple operation needs a lot of checks using standard reflection API. The Invokable class hides this behind the API that is simple to use and very concise.

5. Conclusion

In this article, we were looking at the Guava reflection API and compare it to the standard Java. We saw how to capture generic types at runtime, and how the Invokable class provides elegant and easy to use API for code that is using reflection.

The implementation of all these examples and code snippets can be found in the GitHub project – this is a Maven project, so it should be easy to import and run as it is.