geantyref
geantyref copied to clipboard
GenericTypeReflector.annotate adds invalid annotations
In its current state, GenericTypeReflector.annotate
adds class annotations as type annotations. I'm not entirely sure where this would be useful, but it is a serious problem for almost anything that wants to create annotated classes. See, I'm making a library and I need to create AnnotatedTypes
given a Type and a set of given annotations, however I need those exact annotations, and don't want any of the random @SuppressWarnings
etc. annotations from the class being added to the type on my behalf.
Class
is a Type
, so annotations on the class are type annotations (AnnotationType.TYPE
). This is why they're taken into account when wrapping a class into AnnotatedType
.
Maybe you are referring to TYPE_USE
annotations only?
Anyway, for the moment you can use GenericTypeReflector.replaceAnnotations
to set the exact annotations. As long as you're simply wrapping a Class
this should suffice. And I'll provide a more performant method for wrapping Type
s with the specified annotations only in the next release (which can be soon, if it's blocking you).
Btw, out of curiosity, does GeAnTyRef have the features you're looking for and, if not, which ones do you lack? Asking because I find it a pretty complete solution for working with (annotated) types and advanced reflection, but I of course don't know everyone's use-case.
I don't use it for the "find exact type based on generics" functionality, as that's what Mirror is actually for (Mirror is kinda like GeAnTyRef in that way, but on steroids). I use it primarily for creating Type/AnnotatedType objects. For example, when I call Mirror.reflect(List.class).specialize(Mirror.reflect(String.class)).getCoreType()
I need to dynamically create a ParameterizedType representing List<String>
.
Currently I created a fork that kinda gets the job done, so there isn't much time pressure at the moment. Along with the GenericTypeReflector.annotate
issue, I ran into problems creating AnnotatedParameterizedType
s, but that was remedied with a small patch. Another issue I ran into was the fact that you have a special condition on your ParameterizedType
creation code that requires that the varargs be null, which is syntactically impossible to pass from Kotlin code.
Other than these few hitches though your library has been immensely useful.
Ah, I'll see to fix the null varargs thing as well.
I ran into problems creating AnnotatedParameterizedTypes, but that was remedied with a small patch.
What was wrong there? The same varargs issue or something else?
Mirror is kinda like GeAnTyRef in that way, but on steroids
I'm now curious :) What features are you making?
I use it primarily for creating Type/AnnotatedType objects
So I presume you're mainly making use of TypeFactory
then, rather than GenericTypeReflector
, right?
The existing methods didn’t support owner types. It was as simple as adding a Type
parameter to the annotatedParameterizedClass
method (I believe that was the one) and passing that on to the parameterizedClass
call within it.
Yeah, I’m primarily using those two classes.
From what I can see GeAnTyRef allows you to directly query the resolved types of methods/fields based upon a subclass. In the readme it also only shows getting it from a non-generic type, so I’m not sure if you could, say, get the return value of ArrayList<String>.get
.
What Mirror does is create a whole separate family of objects, mirrors, which closely match the Java Core Reflection classes. These objects can be “specialized”, which means that ArrayList<String>
is a separate object from ArrayList<Integer>
, and the former implements List<String>
and the latter List<Integer>
This allows stuff like this:
class CoolObject<T> extends BoringObject<Set<T>> {}
class BoringObject<T> {
public HashMap<String, ArrayList<T>> thing;
}
ClassMirror coolNumber = Mirror.reflect(new TypeToken<CoolObject<Number>>() {});
// asClassMirror just casts to ClassMirror and is there to avoid the nested casts from hell
TypeMirror listValue =
coolNumber.getField("thing").getType().asClassMirror() // HashMap<String, ArrayList<Set<String>>>
.getTypeParameters()[1].asClassMirror() // ArrayList<Set<String>>
.getTypeParameters()[0].asClassMirror(); // Set<String
assert(listValue == Mirror.reflect(new TypeToken<Set<Number>>() {}));
The existing methods didn’t support owner types
Aah, I see. Will fix! Thanks for reporting these!
I’m not sure if you could, say, get the return value of
ArrayList<String>.get
.
Yes, of course! This is what GeAnTyRef is all about.
Type listOfString = new TypeToken<ArrayList<String>>() {}.getType();
Method get = List.class.getMethod("get", Integer.TYPE);
Type returnType = GenericTypeReflector.getReturnType(get, listOfString);
assertEquals(String.class, returnType);
This allows stuff like this...
Since GeAnTyRef only exposes (and expects) the regular Java interfaces (Type
, AnnotatedType
etc) the API isn't as fluent as in your example, but it can certainly achieve the same result:
Type coolNumber = new TypeToken<CoolObject<Number>>(){}.getType();
Field thing = CoolObject.class.getField("thing");
Type list = ((ParameterizedType) GenericTypeReflector.getFieldType(thing, coolNumber)) // HashMap<String, ArrayList<Set<String>>>
.getActualTypeArguments()[1]; // ArrayList<Set<String>>
Type listValue = ((ParameterizedType) list).getActualTypeArguments()[0]; // Set<String
assertEquals(listValue, new TypeToken<Set<Number>>() {}.getType());
(Same thing also works for AnnotatedType
s in place of Type
s)
I hope this makes your life easier, as you should be able to use GeAnTyRef for more under the hood stuff in your lib, while exposing a nice fluent API. I think that approach sort of combines the strengths of GeAnTyRef and libraries like ClassMate.
ArrayList<String>
is a separate object fromArrayList<Integer>
, and the former implementsList<String>
and the latterList<Integer>
To check if one type extends another GeAnTyRef provides GenericTypeReflector.isSuperType
. So for your example:
Type listOfIntegers = TypeFactory.parameterizedClass(List.class, Integer.class);
Type listOfStrings = TypeFactory.parameterizedClass(List.class, String.class);
Type arrayListOfIntegers = TypeFactory.parameterizedClass(ArrayList.class, Integer.class);
Type arrayListOfStrings = TypeFactory.parameterizedClass(ArrayList.class, String.class);
// ArrayList<X> extends List<X>
assertTrue(GenericTypeReflector.isSuperType(listOfIntegers, arrayListOfIntegers));
assertTrue(GenericTypeReflector.isSuperType(listOfStrings, arrayListOfStrings));
//ArrayList<X> does not extend List<Y>
assertFalse(GenericTypeReflector.isSuperType(listOfStrings, arrayListOfIntegers));
assertFalse(GenericTypeReflector.isSuperType(listOfIntegers, arrayListOfStrings));
Your approach got me thinking... I should maybe provide a nicer API to construct types than what TypeFactory
currently offers. A builder pattern would be more ergonomic.
Something like:
//Concept only, does not exist
TypeBuilder.of(List.class)
.annotations(...)
.parameters(...)
.owner(...)
.build();