Tools for working with generic types

Overview

TypeTools

Build Status Maven Central License JavaDoc

A simple, zero-dependency library for working with types. Supports Java 1.6+ and Android.

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 or reify 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.

Usage

The TypeResolver class provides some of the following methods:

  • Type reify(Type type, Class<S> context)
    Returns a fully reified type using type variable information from the context.
  • Type reify(Type genericType)
    Returns a fully reified genericType using information from the generic declaration.
  • 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:

interface Foo<T1, T2> {}
class Bar implements Foo<Integer, String> {}

Class<?>[] typeArgs = TypeResolver.resolveRawArguments(Foo.class, Bar.class);

assert typeArgs[0] == Integer.class;
assert typeArgs[1] == String.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());

assert typeArgs[0] == String.class;
assert typeArgs[1] == Integer.class;

And from method references:

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

assert typeArg == String.class;

We can reify more complex generic type parameters:

interface Foo<T> {}
class Bar implements Foo<List<Integer>> {}

Type typeArgs = TypeResolver.reify(Foo.class, Bar.class);

ParameterizedType paramType = (ParameterizedType) typeArgs;
Type[] actualTypeArgs = paramType.getActualTypeArguments();
ParameterizedType arg = (ParameterizedType)actualTypeArgs[0];

assert paramType.getRawType() == Foo.class;
assert arg1.getRawType() == List.class;
assert arg1.getActualTypeArguments()[0] == Integer.class;

We can also resolve the raw class for type parameters on fields and methods:

class Entity<ID extends Serializable> {
  ID id;
  void setId(ID id) {}
}

class SomeEntity extends Entity<Long> {}

Type fieldType = Entity.class.getDeclaredField("id").getGenericType();
Type mutatorType = Entity.class.getDeclaredMethod("setId", Serializable.class).getGenericParameterTypes()[0];

assert TypeResolver.resolveRawClass(fieldType, SomeEntity.class) == Long.class;
assert TypeResolver.resolveRawClass(mutatorType, SomeEntity.class) == Long.class;

And we can reify generic type parameters from fields or methods.

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:

class Device {}
class Router extends Device {}

class GenericDAO<T, ID extends Serializable> {
  protected Class<T> persistentClass;
  protected Class<ID> idClass;

  private GenericDAO() {
    Class<?>[] typeArguments = TypeResolver.resolveRawArguments(GenericDAO.class, getClass());
    this.persistentClass = (Class<T>) typeArguments[0];
    this.idClass = (Class<ID>) typeArguments[1];
  }
}

class DeviceDAO<T extends Device> extends GenericDAO<T, Long> {}
class RouterDAO extends DeviceDAO<Router> {}

We can assert that type arguments are resolved as expected:

RouterDAO routerDAO = new RouterDAO();
assert routerDAO.persistentClass == Router.class;
assert routerDAO.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, 9
  • Open JDK 8, 9

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:

interface ExtraFunction<T, R, Z> extends Function<T, R>{}
ExtraFunction<String, Integer, Long> strToInt = s -> Integer.valueOf(s);
Class<?>[] typeArgs = TypeResolver.resolveRawArguments(Function.class, strToInt.getClass());

assert typeArgs[0] == String.class;
assert typeArgs[1] == Integer.class;
assert typeArgs[2] == Unknown.class;

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

On OSGi Support

When using TypeTools in an OSGi environment where lambda or method reference type argument resolution is desired, the sun.reflect system package should be exported to the application bundles. For example, for Felix, add the following to your config.properties file:

org.osgi.framework.system.packages.extra=sun.reflect

Docs

JavaDocs are available here.

License

Copyright 2010-2019 Jonathan Halterman and friends. Released under the Apache 2.0 license.

Comments
  • java.lang.NoClassDefFoundError: sun/reflect/ConstantPool with Java 9

    java.lang.NoClassDefFoundError: sun/reflect/ConstantPool with Java 9

    We would like to use typetools in Mockito to determine lambda's return type. It works fine with Java 8, but our Java 8+ distribution is planned to be Java 9 compatible. Unfortunately typetools seems to not work with Java 9:

    java.lang.NoClassDefFoundError: sun/reflect/ConstantPool
    

    Do you plan to make typetools Java 9 compatible?

    Full stracktrace:

    org.mockito.internal.matchers.AssertionMatcherTest > shouldAllowToUseAssertionInLambda FAILED
        java.lang.NoClassDefFoundError: sun/reflect/ConstantPool
            at net.jodah.typetools.TypeResolver.populateLambdaArgs(TypeResolver.java:379)
            at net.jodah.typetools.TypeResolver.getTypeVariableMap(TypeResolver.java:246)
            at net.jodah.typetools.TypeResolver.resolveRawClass(TypeResolver.java:228)
            at net.jodah.typetools.TypeResolver.resolveRawArguments(TypeResolver.java:165)
            at net.jodah.typetools.TypeResolver.resolveRawArguments(TypeResolver.java:125)
            at org.mockito.internal.util.Primitives.defaultValueForConsumerLambda(Primitives.java:74)
            at org.mockito.AdditionalMatchers.assertArg(AdditionalMatchers.java:1078)
            at org.mockito.internal.matchers.AssertionMatcherTest.shouldAllowToUseAssertionInLambda(AssertionMatcherTest.java:48)
            Caused by:
            java.lang.ClassNotFoundException: sun.reflect.ConstantPool
                at jdk.internal.loader.BuiltinClassLoader.loadClass(java.base@9-ea/BuiltinClassLoader.java:366)
                at jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(java.base@9-ea/ClassLoaders.java:185)
                at java.lang.ClassLoader.loadClass(java.base@9-ea/ClassLoader.java:419)
                ... 8 more
    

    A corresponding Travis build.

    opened by szpak 15
  • Fix lambda inference on openj9 and Java 12 and higher.

    Fix lambda inference on openj9 and Java 12 and higher.

    I fixed the problem in my last PR (I feel like I'd fixed that locally and forgot to add it to git, whoops) and also added support for lambda inference on Java 12 and higher by changing the strategy for circumventing access control on those versions.

    opened by TwilightFlower 11
  • Non-equality on recursive resolved types overflows stack

    Non-equality on recursive resolved types overflows stack

    To start with, I slightly extended a test case by adding two calls to ReifiedParameterizedType#equals. One of them succeeds, since the two passed parameters are referentially equal. However in the second case, I expect equals to return false, since the two parameters are not equal, with the unanticipated failure by stack overflow.

    The reason for this is self-referentiality in ReifiedParameterizedType, which makes equals, toString and probably also hashCode go awry.

    The format of this report is a PR, since I wanted to get across the demonstrating example, but really my intention is to point out an issue in the the reify facility.

    Note that ClassMate opted to go for a special class ResolvedRecursiveType to cover this case.

    opened by lorenzleutgeb 10
  • Reify unbounded recursive types

    Reify unbounded recursive types

    I ran into following issue with the new reify feature. Consider the added test case: typeVariableMap will look like:

    "S" -> "java.lang.Enum<S>"   // where S from both sides are identical
    

    This leads to an infinite recursion and in reality to stack overflow. What is the desired result in this case?

    opened by lorenzleutgeb 9
  • Reification

    Reification

    This is another attempt to resolve #8, meant to supersede #12.

    Different from what @jhalterman originally proposed, this approach does /not/ use type tokens. Rather it traverses common implementations of Type and replaces type variables in there recursively. The benefit I see doing this, is that it is compatible with the existing type hierarchy. A generic parameterized type is just resolved to look like a concrete parameterized type.

    opened by lorenzleutgeb 8
  • #54 Automatic-Module-Name

    #54 Automatic-Module-Name

    This is the pattern I've used with other projects like hamcrest and cucumber to start the ball rolling. I'm currently working on getting cucumber closer to a full jpms build so if 0.6.2 could be released asap once this is merged that would be really useful as it's one of the last dependencies that needed fixing.

    opened by nhojpatrick 7
  • Fix lambda inference on openj9 VM

    Fix lambda inference on openj9 VM

    Currently, lambda generic inference (not really sure what to call it) fails on the OpenJ9 VM since Class.getConstantPool() does not exist. This PR switches to using JavaLangAccess.getConstantPool(Class), which exists on both OpenJ9 and the standard Hotspot VM, and an instance of it can be acquired via sun.misc.SharedSecrets on Java 8 and lower or jdk.internal.misc.SharedSecrets on Java 9 and higher, both of which exist on both OpenJ9 and Hotspot.

    opened by TwilightFlower 6
  • Ignore only the valueOf methods used for boxing.

    Ignore only the valueOf methods used for boxing.

    With this commit all tests are passing when retrolambda is used in combination with IntelliJ & jacoco code coverage. Without this commit the test LambdaTest.shouldResolveSubclassArgumentsForMethodRefs was failing when ran with code coverage after retrolambda was used.

    Basically, we need to check if the valueOf method matches the signature of methods used for boxing operations such as Integer.valueof(int), Boolean.valueOf(boolean) etc.

    Integer class contains 3 valueOf methods out of which only Integer.valueof(int) is used for boxing.

    Thanks

    opened by csoroiu 6
  • How does typetools work for nested generics?

    How does typetools work for nested generics?

    I just ran into this tool today and it looks pretty awesome! I had a question about how typetool handles nested generics. Adapting the example from your readme, suppose I had:

    interface Foo<T1, T2> {}
    class Bar implements Foo<List<Integer>, List<String>> {}
    
    Class<?>[] typeArgs = TypeResolver.resolveRawArguments(Foo.class, Bar.class);
    

    What would typeArgs contain? I'm assuming that it would just contain List.class, correct? All List<T> instances will share the same List.class instance regardless of the type of T due to type-erasure, and there is no way to represent the "class" of a set of strings vs. a set of integers (because that distinction is meaningless). IMO it might be due to the conflation of class-instances with runtime type-tokens. It works in cases where the class instance has a one-to-one mapping (semantically) with the type, but this mapping breaks down when you have a generic class that can work with objects of any type. There is no facility (that I know of) to represent the type of an instance of that generic class that also includes information about its generic parameter(s). Considering that class instances represent... well, class instances, it makes sense to say that the class of List<String> and List<Integer> is just List.class because the List<T> class itself doesn't work on any specific type of object.

    Is there a workaround for this scenario?

    Enhancement 
    opened by vivin 6
  • TypeResolver.resolveRawArguments is not working with lambdas on JDK 14

    TypeResolver.resolveRawArguments is not working with lambdas on JDK 14

    Hi,

    I just found out that the static init block in TypeResolver is choking up on AccessibleObject.class.getDeclaredField("override") in newer JDKs (I think it's actually broken since JDK12, but I don't have it locally).

    Ultimately, this leads to RESOLVES_LAMBDAS not being set and therefore to problems when resolving lambdas in resolveRawArguments. Unfortunately, the static init block is in a try catch where the exception is silently dropped, so this problem isn't really obvious.

    Cheers, Christoph

    opened by dreis2211 5
  • More robust detection when jacoco/IntelliJ coverage is used with retrolambda.

    More robust detection when jacoco/IntelliJ coverage is used with retrolambda.

    1. More robust detection when jacoco/IntelliJ coverage is used with retrolambda.
    2. Added disabled test which fails until constructor reference is fixed. (working on it).
    3. Cosmetic changes - added curly brackets to loops and ifs.

    Not a major change so no need for a new release at the moment. The important change is in TypeTools:463.

    opened by csoroiu 5
  • Enhancements to lambda method detection heuristic

    Enhancements to lambda method detection heuristic

    1. Support method references for methods returning a primitive value
    2. Fix Object class method and constructor reference detection
    3. Fixed resolving lambdas when running with retrolambda & jacoco combined
    opened by csoroiu 1
  • Does not support Android

    Does not support Android

    This does not apply to Android at all: 1.Android class Class has no getConstantPool method 2. Android does not have sun.misc.Unsafe class

    Can run after deleting Unsafe, but it returns Unknown

    opened by weimingjue 0
  • Looking for a co-maintainer

    Looking for a co-maintainer

    As I haven't been able to give TypeTools the timely attention it deserves recently, I'd like to see if anyone would be interested in co-maintaining typetools? If so, send me an e-mail.

    /cc @nhojpatrick @giamma @lorenzleutgeb

    opened by jhalterman 2
  • Java module-info.java

    Java module-info.java

    I'm happy to do the work but after some discussion at apache commons, I would propose a major version bump for adding module-info into typetools.

    1. Any downstream projects using class scanners potentially won't understand module-info so will break projects.
    2. I propose using Multi Release Jar, so; 2.1) Make min java version requirement 1.8 2.2) Update build to have src/main/java11/module-info.java (only that single java11 file) 2.3) Compile module-info.java into META-INF/versions/11/module-info
    3. I don't propose supporting any other versions apart from 1.8 and 11, which are both LTS, afterwards others could be added if useful.
    4. I'm happy to do the alterations and submit a PR for v1.x.x

    Thoughts?

    opened by nhojpatrick 1
  • resolveRawArguments returns empty array

    resolveRawArguments returns empty array

    The following unit test fails because the resolveRawArguments returns an empty array. According to the java doc i would either expect null or a filled array

    public class TypeResolverTest
    {
    	class A
    	{
    		B b;
    	}
    	
    	class B extends HashMap<String,String>
    	{}
    	
    	@Test
    	public void test()
    	{
    		Class[] resolved = TypeResolver.resolveRawArguments( B.class, A.class );
    		assertEquals( 2, resolved.length );
    		assertEquals( String.class, resolved[0] );
    		assertEquals( String.class, resolved[1] );
    	}
    }
    
    opened by bmaassenee 2
  • return 'net.jodah.typetools.TypeResolver$Unknown'

    return 'net.jodah.typetools.TypeResolver$Unknown'

    Hi,bro: I try to get the raw type from lambda expression like this:

    public interface Callback<T> { void handler(List<T> result); }

    Callback<String> callback = result -> { //do something }; Class<?>[] typeArgs = TypeResolver.resolveRawArguments(Callback.class, callback.getClass()); but I get the error type such as 'net.jodah.typetools.TypeResolver$Unknown'

    so,how can I do?

    opened by luckypolaris 1
Releases(typetools-0.4.4)
Owner
Jonathan Halterman
Jonathan Halterman
The open-source Java obfuscation tool working with Ant and Gradle by yWorks - the diagramming experts

yGuard yGuard is an open-source Java obfuscation tool. With yGuard it is easy as pie ( ?? ) to configure obfuscation through an extensive ant task. yG

yWorks GmbH 265 Jan 2, 2023
A harness to build the source code from openjdk.java.net using Free Software tools and dependencies

A harness to build the source code from openjdk.java.net using Free Software tools and dependencies

IcedTea 2 Mar 5, 2022
Generate Java types from JSON or JSON Schema and annotates those types for data-binding with Jackson, Gson, etc

jsonschema2pojo jsonschema2pojo generates Java types from JSON Schema (or example JSON) and can annotate those types for data-binding with Jackson 2.x

Joe Littlejohn 5.9k Jan 5, 2023
The reliable, generic, fast and flexible logging framework for Java.

About logback Thank you for your interest in logback, the reliable, generic, fast and flexible logging library for Java. The Logback documentation can

QOS.CH Sarl 2.6k Jan 7, 2023
Simple but useful generic reload library for Java projects.

SimpleReloadLib Simple Java generic reload library. Introduce SimpleReloadLib used to be a part of QuickShop-Reremake. But it really easy to use and p

Ghost_chu 12 Oct 25, 2022
A generic proxy server for applying access-control policies for a FHIR-store.

FHIR Access Proxy This is a simple access-control proxy that sits in front of a FHIR store (e.g., a HAPI FHIR server, GCP FHIR store, etc.) and contro

Google 17 Jan 5, 2023
GMC-Tools - Plugin with basic tools for Minecraft server administrator

GMC-Tools - Plugin with basic tools for Minecraft server administrator. Currently we do not support configuration files and we do not recommend using this plugin on production servers.

GamesMC Studios 4 Jan 14, 2022
adt4j - Algebraic Data Types for Java

adt4j - Algebraic Data Types for Java This library implements Algebraic Data Types for Java. ADT4J provides annotation processor for @GenerateValueCla

Victor Nazarov 136 Aug 25, 2022
Java 8 annotation processor and framework for deriving algebraic data types constructors, pattern-matching, folds, optics and typeclasses.

Derive4J: Java 8 annotation processor for deriving algebraic data types constructors, pattern matching and more! tl;dr Show me how to write, say, the

null 543 Nov 23, 2022
An annotation-based Java library for creating Thrift serializable types and services.

Drift Drift is an easy-to-use, annotation-based Java library for creating Thrift clients and serializable types. The client library is similar to JAX-

null 225 Dec 24, 2022
Java 8 annotation processor and framework for deriving algebraic data types constructors, pattern-matching, folds, optics and typeclasses.

Derive4J: Java 8 annotation processor for deriving algebraic data types constructors, pattern matching and more! tl;dr Show me how to write, say, the

null 543 Nov 23, 2022
Hi, Spring fans! In this installment, we're going to look at some the C in M-V-C and their representation in Spring's `@Controller` types!

@Controllers Hi, Spring fans! In this installment, we're going to look at some the C in M-V-C and their representation in Spring's @Controller types!

Spring Tips 22 Nov 19, 2022
A universal types-preserving Java serialization library that can convert arbitrary Java Objects into JSON and back

A universal types-preserving Java serialization library that can convert arbitrary Java Objects into JSON and back, with a transparent support of any kind of self-references and with a full Java 9 compatibility.

Andrey Mogilev 9 Dec 30, 2021
The Apache Commons CSV library provides a simple interface for reading and writing CSV files of various types.

Apache Commons CSV The Apache Commons CSV library provides a simple interface for reading and writing CSV files of various types. Documentation More i

The Apache Software Foundation 307 Dec 26, 2022
SpringBoot based return value types are supported by browsers

SpringBoot based return value types are supported by browsers

Elone Hoo 5 Jun 24, 2022
Algorithms of all types of sorting

Sorting-Algorithms Algorithms of all types of sorting. Bubble_sort.java Bucket_sort.java Cocktail_sort.java Comb_sort.java Counting_sort.java Cycle_so

Ashish Kumar 19 Nov 21, 2022
A command-line tool to generate different types of noise as images.

noisegen A command-line tool to generate different types of noise as images. Usage Run one of the releases, either the JAR using java -jar noisegen-0.

Tommy Ettinger 6 Jul 21, 2022
The open-source Java obfuscation tool working with Ant and Gradle by yWorks - the diagramming experts

yGuard yGuard is an open-source Java obfuscation tool. With yGuard it is easy as pie ( ?? ) to configure obfuscation through an extensive ant task. yG

yWorks GmbH 265 Jan 2, 2023
OpenRefine is a free, open source power tool for working with messy data and improving it

OpenRefine OpenRefine is a Java-based power tool that allows you to load data, understand it, clean it up, reconcile it, and augment it with data comi

OpenRefine 9.2k Jan 1, 2023