Skip to content

Repository files navigation

TypeTools Build Status

A simple, zero-dependency set of tools for working with Java types.

Introduction

One of the sore points with Java involves working with type information. In particular, Java's generics do not provide a way to resolve the type information for a given class. TypeTools looks to solve this by fully resolving generic type information declared on any class, interface, lambda expression or method.

Setup

Add TypeTools as a Maven dependency:

<dependency>
<groupId>net.jodah</groupId>
<artifactId>typetools</artifactId>
<version>0.4.0</version>
</dependency>

Usage

The TypeResolver class provides the following methods:

  • Class<?>[] resolveRawArguments(Class<T> type, Class<S> subType)
    Resolves the raw arguments for a type using type variable information from a subType.
  • Class<?> resolveRawArgument(Class<T> type, Class<S> subType)
    Resolves the raw argument for a type using type variable information from a subType.
  • Type resolveGenericType(Class<?> type, Type subType)
    Resolves the generic type using type variable information from a subType.
  • Class<?> resolveRawClass(Type genericType, Class<?> subType)
    Resolves the raw class for a genericType using type variable information from a subType.

Examples

A typical use case is to resolve arguments for a type, given a sub-type:

interfaceFoo<T1, T2> {}
classBarimplementsFoo<HashSet<Integer>, ArrayList<String>> {}
Class<?>[] typeArgs = TypeResolver.resolveRawArguments(Foo.class, Bar.class);
asserttypeArgs[0] == HashSet.class;
asserttypeArgs[1] == ArrayList.class;

Type arguments can also be resolved from lambda expressions:

Function<String, Integer> strToInt = s -> Integer.valueOf(s);
Class<?>[] typeArgs = TypeResolver.resolveRawArguments(Function.class, strToInt.getClass());
asserttypeArgs[0] == String.class;
asserttypeArgs[1] == Integer.class;

And from method references:

Comparator<String> comparator = String::compareToIgnoreCase;
Class<?> typeArg = TypeResolver.resolveRawArgument(Comparator, comparator.getClass());
asserttypeArg == String.class;

We can also resolve the raw class for any generic type, given a sub-type:

classEntity<IDextendsSerializable> {
IDid;
voidsetId(IDid) {}
}
classSomeEntityextendsEntity<Long> {}
TypefieldType = Entity.class.getDeclaredField("id").getGenericType();
TypemutatorType = Entity.class.getDeclaredMethod("setId", Serializable.class).getGenericParameterTypes()[0];
assertTypeResolver.resolveRawClass(fieldType, SomeEntity.class) == Long.class;
assertTypeResolver.resolveRawClass(mutatorType, SomeEntity.class) == Long.class;

Common Use Cases

Layer supertypes often utilize type parameters that are populated by subclasses. A common use case for TypeTools is to resolve the type arguments for a layer supertype given a sub-type.

Following is an example Generic DAO layer supertype implementation:

classDevice {}
classRouterextendsDevice {}
classGenericDAO<T, IDextendsSerializable> {
protectedClass<T> persistentClass;
protectedClass<ID> idClass;
privateGenericDAO() {
Class<?>[] typeArguments = TypeResolver.resolveRawArguments(GenericDAO.class, getClass());
this.persistentClass = (Class<T>) typeArguments[0];
this.idClass = (Class<ID>) typeArguments[1];
}
}
classDeviceDAO<TextendsDevice> extendsGenericDAO<T, Long> {}
classRouterDAOextendsDeviceDAO<Router> {}

We can assert that type arguments are resolved as expected:

RouterDAOrouterDAO = newRouterDAO();
assertrouterDAO.persistentClass == Router.class;
assertrouterDAO.idClass == Long.class;

Additional Features

By default, type variable information for each resolved type is weakly cached by the TypeResolver. Caching can be enabled/disabled via:

TypeResolver.enableCache();
TypeResolver.disableCache();

Additional Notes

On Lambda Support

Lambda type argument resolution is currently supported for:

  • Oracle JDK 8
  • Open JDK 8

On Unresolvable Lambda Type Arguments

When resolving type arguments with lambda expressions, only type parameters used in the functional interface's method signature can be resolved. Ex:

interfaceExtraFunction<T, R, Z> extendsFunction<T, R>{}
ExtraFunction<String, Integer, Long> strToInt = s -> Integer.valueOf(s);
Class<?>[] typeArgs = TypeResolver.resolveRawArguments(Function.class, strToInt.getClass());
asserttypeArgs[0] == String.class;
asserttypeArgs[1] == Integer.class;
asserttypeArgs[2] == Unknown.class;

Since the type parameter Z in this example is unused by Function, its argument resolves to Unknown.class.

Docs

JavaDocs are available here.

License

Copyright 2010-2015 Jonathan Halterman - Released under the Apache 2.0 license.

About

Tools for resolving generic types

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages