| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
A fork of the excellent GenTyRef library, adding support for working with AnnotatedTypes introduced in Java 8 plus many nifty features.
Table of Contents
This library aims to provide a simple way to analyse generic type information and dynamically create (Annotated)Type instances, all at runtime.
All functionality of the library is exposed via a handful of classes:
<dependency>
<groupId>io.leangen.geantyref</groupId>
<artifactId>geantyref</artifactId>
<version>2.0.0</version>
</dependency>You can find instructions at maven.org
The simplest example would be having a class similar to this:
class StringList extends ArrayList<String> {
...
}Getting the exact return type of StringList's get method is rather difficult:
Method get = StringList.class.getMethod("get", int.class);
get.getGenericReturnType() //yields T, which is not very useful informationOn the other hand, running GenericTypeReflector.getExactReturnType(get, StringClass.class) would yield String which is what we were looking for.
Presume we have two simple classes:
class Container<T> {
public T item;
}
class NumberContainer extends Container<Number> {}We again face issues when trying to discover the exact type of the item field:
Field item = NumberContainer.class.getField("item");
item.getGenericType(); //yields T againInstead, GenericTypeReflector.getExactFieldType(item, NumberContainer.class) returns Number as desired.
GenericTypeReflector.getExactParameterTypes(methodOrConstructor, aType)
If we had the classes defined as follows:
class Container<T> {
public T item;
}
class NumberContainer<T extends Number> extends Container<T> {}
class LongContainer extends NumberContainer<Long> {}If we'd call LongContainer.class.getGenericSuperclass() it would correctly return NumberContainer<Long> but getting from there to Container<Long> is much more difficult, as there's no direct way.
GeantyRef allows us to simply call GenericTypeReflector.getExactSuperType(LongContainer.class, Container.class) to get Container<Long>
Even more interestingly, it is sometimes possible to get the exact sub type of a type. For example, if we had List<String> and we wanted ArrayList<String> if would be possible, as the ArrayList's sole type parameter is coming from List, i.e. ArrayList does not define any type parameters itself, List<String> already contains all the needed information. This is rather difficult to calculate using standard reflection, but if we already had a reference to List<String> called listOfString, it is enough to call:
GenericTypeReflector.getExactSubType(listOfString, ArrayList.class) to get ArrayList<String>
Still, how to get to listOfString? Probably by calling one of the methods described above. But, it's also possible to create a type literal directly via TypeToken, or construct the desired Type (List<String>) dynamically using TypeFactory.
It is worth noting that all the basic methods on the GenericTypeReflector listed above have overloads that work with AnnotatedTypes.
class Person {
public List<@NonNull String> nicknames;
}
AnnotatedType listOfNonNullStrings = Person.class.getField("nicknames").getAnnotatedType();
Method get = List.class.getMethod("get", int.class);
//returns an AnnotatedType representing: @NonNull String
AnnotatedType nonNullString = GenericTypeReflector.getExactReturnType(get, listOfNonNullStrings);Similarly, getExactFieldType, getExactParameterTypes, getExactSuperType, getExactSubType work with AnnotatedTypes.
This approach, known as Typesafe Heterogenous Container (THC) or type token, is widely used in libraries like Jackson or Gson that need to work with generic types. There are various sources describing the intricacies of this approach, Neal Gafter's blog being a classic one.
To obtain a Type instance representing a know generic type, such as List<String> it is enough to do the following:
Type listOfString = new TypeToken<List<String>>(){}.getType();
What we're doing here is creating an anonymous subclass of a parameterized type (TypeToken) and getting it's generic super class via getGenericSuperclass.
If instead we wanted an instance of an annotated type, such as List<@NonNull String>, the same principle would apply:
AnnotatedType listOfNonNullString = new TypeToken<List<@NonNull String>>(){}.getAnnotatedType();
TypeToken is only useful if all the generic type information is known ahead of time. It will not allow us to create a Type or AnnotatedType dynamically. For such a task, TypeFactory provides adequate methods.
Class<List> listType = List.class;
Class<String> typeParameter = String.class;
//returns a Type representing List<String>
Type listOfStrings = TypeFactory.parameterizedClass(listType, typeParameter);
Class<Map> mapType = Map.class;
Class<String> keyTypeParameter = String.class;
Class<Number> valueTypeParameter = Number.class;
//returns a Type representing Map<String, Number>
Type mapOfStringNumber = TypeFactory.parameterizedClass(mapType, keyTypeParameter, valueTypeParameter);TypeFactory can also produce AnnotatedType instances, but this means we need to somehow obtain instances of the annotations themselves (instances of Annotation). An obvious way of getting them would be from an existing AnnotatedElement (annotatedElement.getAnnotations()). Class, Parameter, Method, Constructor, Field all implement AnnotatedElement.
But, TypeFactory also allows you to instantiate an Annotation dynamically:
Map<String, Object> annotationParameters = new HashMap<>();
annotationParameters.put("name", "someName");
MyAnnotation myAnnotation = TypeFactory.annotation(MyAnnotation.class, annotationParameters);This produces an Annotation instance as if @MyAnnotation(name = "someName") was found in the sources.
Armed with this knowledge, it's easy to produce an AnnotatedType:
Class<List> listType = List.class;
Class<String> typeParameter = String.class;
Annotation[] annotations = { myAnnotation };
//returns an AnnotatedType representing: @MyAnnotation(name = "someName") List<String>
AnnotatedType annotatedListOfString = TypeFactory.parameterizedClass(listType, annotations, typeParameter);GenericTypeReflector also allows you to wrap any Type into an AnnotatedType by collecting its direct annotations. For example:
@SuppressWarnings("unchecked")
class Something {}
//returns an AnnotatedType representing: @SuppressWarnings("unchecked") Something
AnnotatedType something = GenericTypeReflector.annotate(Something.class);This method will correctly turn a ParameterizedType into an AnnotatedParameterizedType, WildcardType into an AnnotatedWildcardType etc.
To turn a ParameterizedType into an AnnotatedParameterizedType with customized annotations:
Type listOfStrings = TypeFactory.parameterizedClass(List.class, String.class);
Annotation[] typeAnnotations = { myAnnotation };
Annotation[] argumentAnnotations = { anotherAnnotation };
//Get an AnnotatedParameterizedType representing: @MyAnnotation List<@AnotherAnnotation String>
AnnotatedParameterizedType annotatedListOfAnnotatedStrings =
parameterizedAnnotatedType(listOfStrings, typeAnnotations, argumentAnnotations);There are more features provided by GenericTypeReflector that were not described in depth here, like the possibility to replace annotations on an AnnotatedType via replaceAnnotations, or update them via updateAnnotations, calculate hash codes and check equality of AnnoatedTypes (as equals and hasCode are not overridden in Java's AnnotatedType implementations) etc, so feel free to explore a bit on your own.
More info can be found at the project Wiki.
| Back | FazBrowse Home | New Git URL |