Skip to content

Getting started

Installation

Maven Central

Maven:

<dependency>
  <groupId>ru.vyarus</groupId>
  <artifactId>generics-resolver</artifactId>
  <version>3.0.3</version>
</dependency>

Gradle:

implementation 'ru.vyarus:generics-resolver:3.0.3'

Requires java 6 or above (compatible with java 11).

Usage

See java reflection tutorial if you have problems with reflection (minimal reflection knowledge is required for usage).

Suppose we have class hierarchy:

public class Base<T, K> {  

  private List<K> fld;   

  T doSomething(K arg) {...}
}

public class Root extends Base<Integer, Long> {...}

Base class generics could only be known in context of extending class (declared generics). Generics context must be created (from root class):

// compute generics for classes in Root hierarchy
GenericsContext context = GenericsResolver.resolve(Root.class)
        // switch current class to Base (to work in context of it)
        .type(Base.class);

Hint

All generics in the root class hierarchy are resolved immediately and resolution data is cached internally, so it is cheap to call generics resolution for the same type in multiple places.

Getting Base class generics (in context of Root):

context.generics() == [Integer.class, Long.class]

Resolving methods:

//  T doSomething(K arg)
MethodGenericsContext methodContext = context
    .method(Base.class.getMethod("doSomething", Object.class))     

// method return class (in context of Root)
methodContext.resolveReturnClass() == Integer.class

// method parameters (in context of Root)
methodContext.resolveParameters() == [Long.class]

The same way fields, constructors and even direct types could be resolved.

Generics context assumed to be used during reflection introspection to provide additional types information.

Note

It is important to always properly switch context (with .type()) in order to correctly solve types.
BUT if you work with fields, methods or constructors, context will be switched automatically. For example, field resolution:

GenericsResolver.resolve(Root.class)
    .resolveFieldsType(Base.class.getDeclaredField('fld')) == List<Long>

Note that here we did not do manual context switch to Base class, because field contains declaration type and so countext could switched automatically.

Utilities

There are 2 API levels:

  • Context API (primary) used to support introspection (reflection analysis) and direct utilities. Context API is safe because it always performs compatibility checks and throws descriptive exceptions.
  • Static utilities. With utilities, usage errors are possible (quite possible to use wrong generics map), but in simple cases it's not a problem. Use API that fits best your use case.

How to learn

Library is pretty low-level, so it would be easier to first look on usage examples to find exact cases and only after that go to theory section for details.