Coder Social home page Coder Social logo

leangen / geantyref Goto Github PK

View Code? Open in Web Editor NEW

This project forked from coekie/gentyref

95.0 7.0 14.0 408 KB

Advanced generic type reflection library with support for working with AnnotatedTypes (for Java 8+)

License: Apache License 2.0

Java 100.00%
reflection types annotations

geantyref's Introduction

GeantyRef

Maven Central Javadoc Build Status License

A fork of the excellent GenTyRef library, adding support for working with AnnotatedTypes introduced in Java 8 plus many nifty features.

Table of Contents

Goal

This library aims to provide a simple way to analyse generic type information and dynamically create (Annotated)Type instances, all at runtime.

Overview

All functionality of the library is exposed via a handful of classes:

  • GenericTypeReflector : contains static methods used for generic type analysis
  • TypeFactory : contains static methods used for Type/AnnotatedType instance creation
  • TypeToken : Used to create Type/AnnotatedType literals (using THC pattern)

Usage

Maven:

<dependency>
    <groupId>io.leangen.geantyref</groupId>
    <artifactId>geantyref</artifactId>
    <version>1.3.15</version>
</dependency>

Other build tools:

You can find instructions at maven.org

Examples

Getting the exact return type of a method

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 information

On the other hand, running GenericTypeReflector.getExactReturnType(get, StringClass.class) would yield String which is what we were looking for.

Getting the exact type of a field

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 again

Instead, GenericTypeReflector.getExactFieldType(item, NumberContainer.class) returns Number as desired.

Getting the exact types of method parameters

GenericTypeReflector.getExactParameterTypes(methodOrConstructor, aType)

Getting the exact super type

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>

Getting the exact sub type

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.

Getting annotated return/parameter/field/sub/super types

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.

Creating type literals using TypeToken

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.

Creating annotated type literals using TypeToken

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();

Creating types dynamically using TypeFactory

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);

Creating annotated types dynamically using TypeFactory

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);

Turning any Type into an AnnotatedType

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);

More

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.

Wiki

More info can be found at the project Wiki.

License

Apache 2.0

geantyref's People

Contributors

coekie avatar gselzer avatar hgschmie avatar kaqqao avatar mmerruko avatar solonovamax avatar stevenschlansker avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

geantyref's Issues

Add `module-info.java`

It would be cool to be able to use Geantyref from Modular Java, i.e. with tools like jlink. But right now, it is not possible, because Geantyref ships an Automatic-Module-Name, instead of a module-info.java.

reduceBounded StackOverflowError with self-recursing capture

Hi @kaqqao happy new year,

We received a bug report in Jdbi: jdbi/jdbi#2582
It looks to actually be a bug in geantyref.

Here's the code to reproduce:

public interface UnitX<Q extends QuantityX<Q>> {
}
public interface QuantityX<Q extends QuantityX<Q>> {
}
class TestClass {
    public UnitX<?> returnType() {
        return null;
    }
}

GenericTypeReflector.reduceBounded(GenericTypeReflector.annotate(
        GenericTypeReflector.getExactReturnType(TestClass.class.getMethod("returnType"), TestClass.class)));

This results in a StackOverflowError. Thank you for any insight you can provide!

Mitigate Java's annotation parser bug where possible

Java versions before 11 (?) have multiple bugs in the annotation parser (e.g. this one) causing it to lose annotations on nested types.

It is in some cases possible to recover the lost class-level annotations when transforming a type to its canonical form, and GenericTypeReflector#toCanonical should take advantage of these mitigations where applicable.

Calling hashCode on two equal `TypeToken`s returns different results

According to the javadocs for java.lang.Object#hashCode,

If two objects are equal according to the equals method, then calling the hashCode method on each of the two objects must produce the same integer result.

Currently, TypeToken does not abide by this.
It is possible to get two type tokens where first.equals(second) is true, but first.hashCode() == second.hashCode() is false.
This violates the contract of hashCode and breaks any attempt to use TypeToken as the key to a HashMap.

Here is some example code that can reproduce the issue:

import io.leangen.geantyref.TypeToken;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class Test {
    public static void main(String[] args) throws NoSuchMethodException {
        TypeToken<Set<? extends Test>> testTypeToken = new TypeToken<>() {};
        Method method = Test.class.getMethod("testFunction", Set.class);
        TypeToken<?> otherTypeToken = TypeToken.get(method.getParameters()[0].getParameterizedType());

        Map<TypeToken<?>, String> testMap = new HashMap<>();
        testMap.put(otherTypeToken, "test");

        System.out.println("testTypeToken: " + testTypeToken);
        System.out.println("testTypeToken.getType(): " + testTypeToken.getType());
        System.out.println("testTypeToken.hashCode(): " + testTypeToken.hashCode());
        System.out.println();
        System.out.println("otherTypeToken: " + otherTypeToken);
        System.out.println("otherTypeToken.getType(): " + otherTypeToken.getType());
        System.out.println("otherTypeToken.hashCode(): " + otherTypeToken.hashCode());
        System.out.println();
        System.out.println("testTypeToken.equals(otherTypeToken): " + testTypeToken.equals(otherTypeToken));
        System.out.println("otherTypeToken.equals(testTypeToken): " + otherTypeToken.equals(testTypeToken));
        System.out.println();
        System.out.println("testMap: " + testMap);
        System.out.println("testMap.get(testTypeToken): " + testMap.get(testTypeToken));
    }

    public static void testFunction(Set<? extends Test> testSet) {}
}

The current output of this program is:

testTypeToken: Test$1@60f4877b
testTypeToken.getType(): java.util.Set<? extends Test>
testTypeToken.hashCode(): 1626638203

otherTypeToken: io.leangen.geantyref.TypeToken$2@17a57871
otherTypeToken.getType(): java.util.Set<? extends Test>
otherTypeToken.hashCode(): 396720241

testTypeToken.equals(otherTypeToken): true
otherTypeToken.equals(testTypeToken): true

testMap: {io.leangen.geantyref.TypeToken$2@17a57871=test}
testMap.get(testTypeToken): null

The expected output is:

 testTypeToken: Test$1@60f4877b
 testTypeToken.getType(): java.util.Set<? extends Test>
-testTypeToken.hashCode(): 1626638203
+testTypeToken.hashCode(): 396720241

 otherTypeToken: io.leangen.geantyref.TypeToken$2@17a57871
 otherTypeToken.getType(): java.util.Set<? extends Test>
 otherTypeToken.hashCode(): 396720241

 testTypeToken.equals(otherTypeToken): true
 otherTypeToken.equals(testTypeToken): true

 testMap: {io.leangen.geantyref.TypeToken$2@17a57871=test}
-testMap.get(testTypeToken): null
+testMap.get(testTypeToken): "test"

As you can see, if reflection is used to get the parameterized type of a method, and that type is then added to a hash map, you cannot access that parameterized type using one constructed via new TypeToken<Set<? extends Test>>() {}.

Add a way to resolve bounded types directly

Coming from coekie#5 (comment)

I gave it a spin and came up with this:

  public static Type getResolvedReturnType(Method m, Type declaringType) {
    return resolveTypeVariableIfNecessary(GenericTypeReflector.getReturnType(m, declaringType));
  }

  public static Type getResolvedFieldType(Field f, Type declaringType) {
    return resolveTypeVariableIfNecessary(GenericTypeReflector.getFieldType(f, declaringType));
  }

  public static List<Type> getResolvedParameterTypes(Method m, Type declaringType) {
    return Arrays.stream(GenericTypeReflector.getParameterTypes(m, declaringType))
      .map(ReflectionUtil::resolveTypeVariableIfNecessary)
      .collect(Collectors.toList());
  }

  private static Type resolveTypeVariableIfNecessary(Type returnType) {
    if (returnType instanceof TypeVariable) {
      AnnotatedType annotatedType = GenericTypeReflector.annotate(returnType);
      return GenericTypeReflector.reduceBounded(annotatedType).getType();
    }
    return returnType;
  }

it seems to work, but I'm not sure its the best way. You can see the full change here https://github.com/leonard84/spock/tree/replace_gentyref_with_geantyref

I think that it might have value to have something like this in GenericTypeReflector directly, this way it could be optimized as well, the current implementation has to re-annotate the type that was discarded in GenericTypeReflector internally.

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.

Type parameters don't resolve when enclosing class has a type parameter

Hi @kaqqao , we ran into some behavior that we don't understand over at jdbi/jdbi#2306

    @Test
    void testGenericTypeWorksInParameterizedClasses() {
        abstract class TypeCapture<T> {
            private final Type type;

            protected TypeCapture() {
                this.type = GenericTypeReflector.getTypeParameter(getClass(), TypeCapture.class.getTypeParameters()[0]);
            }

            public final Type getType() {
                return type;
            }
        }

        class Foo<T> {
            void checkType() {
                assertThat(new TypeCapture<List<String>>() {}.getType()).isEqualTo(TypeFactory.parameterizedClass(List.class, String.class));
            }
        }

        new Foo<>().checkType();
    }

This code works as you'd expect when the enclosing class Foo has no type parameter - but when you introduce a parameter Foo<T>, suddenly the type parameter is lost in the TypeCapture class and this test fails.

Is this a bug in GeAnTyRef, or are we perhaps using it incorrectly? Thank you!

getExactParameterTypes() for constructors

I would be useful to support getExactParameterTypes() not only for methods but also for constructors.

Given that geantyref already requires Java 1.8+, the easiest way to accomplish this is to change the method signature

public static AnnotatedType[] getExactParameterTypes(Method m, AnnotatedType declaringType)

to

public static AnnotatedType[] getExactParameterTypes(Executable m, AnnotatedType declaringType)

Likewise for the Type overload. No other changes required.

PS: Since Type overloads are implemented in terms of AnnotatedType overloads but discard annotation information, would it be more efficient to lift Type to AnnotatedType using new AnnotatedTypeImpl(type) rather than annotate(type)?

Provide a more general type resolution facility

Provide a way to fully or partially resolve a type in the context of another.
E.g. given:

class A<T> {
    Set<T> get();
}

class B extends A<String> {}

there should be a way to resolve Set<T> in the context of B (without having a reference to the get method itself).

This aims to solve the issue outlined in #5

compiling with JDK 11 leads to test failures

It seems that the toString() method for an annotation has changed post-JDK8. When compiling with JDK 11, set to JDK 8 compatibility, the type factory test fails here:

Screenshot 2024-01-19 at 12 13 40

JDK 11 adds the "" empty string to the definition when calling Annotation#toString(), while JDK8 does not.

Unexpected exception when erasing method return type from a wildcard generic class

Hi @kaqqao , thank you again for this great library. I think I might have found a bug:

@Test
public void testErase() throws Exception {
    GenericTypeReflector.erase(
            GenericTypeReflector.getExactReturnType(
                    E2.class.getMethod("getVal"),
                    GenericTypeReflector.addWildcardParameters(E2.class)));
}

public static class E2<T extends String> {
    private final T val;
    E2(final T val) {
        this.val = val;
    }
    public T getVal() {
        return val;
    }
}

Expected result: String.class
Actual result: RuntimeException

java.lang.RuntimeException: not supported: class io.leangen.geantyref.CaptureTypeImpl
	at io.leangen.geantyref.GenericTypeReflector.erase(GenericTypeReflector.java:87)
	at org.jdbi.v3.core.mapper.TestEnums.testErase(TestEnums.java:111)

ParameterizedTypeImpl `toString()` method returns wrong method name for nested classes

Consider this:

Screenshot 2024-01-19 at 12 17 55

While the ParameterizedTypeImpl (which is part of geantryref) returns io.leangen.geantyref.Issue20Test.UnitX<io.leangen.geantyref.Issue20Test$QuantityX>, the actual class name is io.leangen.geantyref.Issue20Test$UnitX as UnitX is a nested class within the Issue20Test class. But the toString() method omits the $ as a marker for a nested class and uses a .

No module name described in MANIFEST

For those using JPMS, it would be nice if geantyref contained an Automatic Module Name (see here for reasons why). This would provide a stable module name that other projects can reference, and is a very small change on the part of geantyref (see here).

GenericTypeReflector.transform visitor visitVariable cannot change type away from variable type

Hi @kaqqao thank you for your continued help, I wonder what you think of this.

Given the class,

public class GenericBean<T extends String> {
    public List<T> getProperty() {
        return Collections.emptyList();
    }
}

I want to figure out the most specific type for the return value of getProperty given that I don't have the parameterized GenericBean<T>, only GenericBean.class - replacing T with String (the bound) and then figure out List<String> as the property type.

To do this, I am trying to use GenericTypeReflector.transform and override visitVariable to replace variables with their bound.

private AnnotatedType resolveBounds(final Type type) {
    return GenericTypeReflector.transform(GenericTypeReflector.annotate(type), new TypeVisitor() {
        @Override
        protected AnnotatedType visitVariable(final AnnotatedTypeVariable type) {
            return type.getAnnotatedBounds()[0]; // T extends String -> String
        }
    });
}

        assertThat(Arrays.stream(GenericBean.class.getTypeParameters())
                .map(this::resolveBounds)
                .toArray(AnnotatedType[]::new))
            .containsExactly(GenericTypeReflector.annotate(String.class));

Unfortunately, despite this being statically safe, it does not actually work:

java.lang.ClassCastException: class sun.reflect.annotation.AnnotatedTypeFactory$AnnotatedTypeBaseImpl cannot be cast to class java.lang.reflect.AnnotatedTypeVariable (sun.reflect.annotation.AnnotatedTypeFactory$AnnotatedTypeBaseImpl and java.lang.reflect.AnnotatedTypeVariable are in module java.base of loader 'bootstrap')
	at io.leangen.geantyref.TypeVisitor.visitCaptureType(TypeVisitor.java:75)
	at io.leangen.geantyref.GenericTypeReflector.transform(GenericTypeReflector.java:1129)
	at io.leangen.geantyref.TypeVisitor.lambda$visitParameterizedType$0(TypeVisitor.java:27)
...
	at io.leangen.geantyref.TypeVisitor.visitParameterizedType(TypeVisitor.java:28)
	at io.leangen.geantyref.GenericTypeReflector.transform(GenericTypeReflector.java:1117)
	at org.jdbi.v3.core.argument.TestBeanArguments.resolveBounds(TestBeanArguments.java:292)
	at org.jdbi.v3.core.argument.TestBeanArguments.wut(TestBeanArguments.java:284)

It looks like the visitor assumes it can cast the result:

AnnotatedCaptureType annotatedCapture = new AnnotatedCaptureTypeImpl((CaptureType) type.getType(),
				(AnnotatedWildcardType) transform(type.getAnnotatedWildcardType(), this),
				(AnnotatedTypeVariable) transform(type.getAnnotatedTypeVariable(), this),
				null, type.getAnnotations());

Please let me know if I'm on the wrong track, or if there's something I'm missing. Thank you!

StackOverflowError in VarMap.map when calling GenericTypeReflector.getParameterTypes()

mfriedenhagen reported an issue in Spock spockframework/spock#1909 where geantyref throws a StackOverflowError.

I have removed the Spock dependency from the reproducer attached to the Spock issue, and the error is still there:

import java.lang.reflect.Method;
import java.util.Objects;

public class StackOverflowReproducer {

  static abstract class Status<StatusT, StatusBuilderT> {
    static class Builder<StatusT, StatusBuilderT> {
    }
  }

  static abstract class Resource<StatusT, StatusBuilderT> {
  }

  static class ProjectValidator {
    public <
      StatusT extends Status<StatusT, StatusBuilderT>,
      StatusBuilderT extends Status.Builder<StatusT, StatusBuilderT>,
      T extends Resource<StatusT, StatusBuilderT>>
    void validate(long id, Class<T> klass) {
      throw new UnsupportedOperationException("Just for testing");
    }
  }

  public static void main(String[] args) throws NoSuchMethodException {
    Class<?> cls = ProjectValidator.class;
    Method method = Objects.requireNonNull(cls.getMethod("validate", long.class, Class.class));
    io.leangen.geantyref.GenericTypeReflector.getParameterTypes(method, cls);
  }
}

The exception stack looks like:

java.lang.StackOverflowError
	at java.base/sun.reflect.generics.reflectiveObjects.TypeVariableImpl.getGenericDeclaration(TypeVariableImpl.java:144)
	at java.base/sun.reflect.generics.reflectiveObjects.TypeVariableImpl.typeVarIndex(TypeVariableImpl.java:227)
	at java.base/sun.reflect.generics.reflectiveObjects.TypeVariableImpl.getAnnotatedBounds(TypeVariableImpl.java:220)
	at java.base/sun.reflect.annotation.AnnotatedTypeFactory$AnnotatedTypeVariableImpl.getAnnotatedBounds(AnnotatedTypeFactory.java:383)
	at io.leangen.geantyref.VarMap.map(VarMap.java:98)
	at io.leangen.geantyref.VarMap.map(VarMap.java:115)
	at io.leangen.geantyref.VarMap.map(VarMap.java:148)
	at io.leangen.geantyref.VarMap.map(VarMap.java:98)

The used geantyref version is io.leangen.geantyref:geantyref:1.3.15
Tested for Java 8 and 17

Current trunk does not build for JDK 11 and has test failures for JDK 8

With JDK 11:

[ERROR] /Users/henning/oss/geantyref/src/main/java/io/leangen/geantyref/AnnotatedArrayTypeImpl.java:[13,1] io.leangen.geantyref.AnnotatedArrayTypeImpl is not abstract and does not override abstract method getAnnotatedOwnerType() in java.lang.reflect.AnnotatedArrayType
[ERROR] /Users/henning/oss/geantyref/src/main/java/io/leangen/geantyref/AnnotatedTypeVariableImpl.java:[13,1] io.leangen.geantyref.AnnotatedTypeVariableImpl is not abstract and does not override abstract method getAnnotatedOwnerType() in java.lang.reflect.AnnotatedTypeVariable
[ERROR] /Users/henning/oss/geantyref/src/main/java/io/leangen/geantyref/AnnotatedParameterizedTypeImpl.java:[15,1] io.leangen.geantyref.AnnotatedParameterizedTypeImpl is not abstract and does not override abstract method getAnnotatedOwnerType() in java.lang.reflect.AnnotatedParameterizedType
[ERROR] /Users/henning/oss/geantyref/src/main/java/io/leangen/geantyref/AnnotatedWildcardTypeImpl.java:[16,1] io.leangen.geantyref.AnnotatedWildcardTypeImpl is not abstract and does not override abstract method getAnnotatedOwnerType() in java.lang.reflect.AnnotatedWildcardType

With JDK 8, the current code compiles but shows errors:

  • [ERROR] Issue17Test.testTypeLiteralUsingTypeToken:25 Both TypeTokens should have equal hash codes expected:<391447681> but was:<148218870> (fixed by #21)
  • [ERROR] Issue20Test.testRecursiveTypes:60 array lengths differed, expected.length=5 actual.length=2; arrays first differed at element [0]; expected:<interface io.leangen.geantyref.Annotations$A1> but was:<interface io.leangen.geantyref.Annotations$A2>
  • [ERROR] TypeFactoryTest.testAnnotationEqualityAndHashCode:597 expected:<...eFactoryTest$A1(def=[, longs=[2, 4, 6], value=Test value, values=[value 1, value 2]])> but was:<...eFactoryTest$A1(def=["", longs={2L, 4L, 6L}, value="Test value", values={"value 1", "value 2"}])>

Inconsistent results when using TypeToken to get AnnotatedType in static and instance methods

jdk version

1.8

example

package com.zyc;

import io.leangen.geantyref.TypeToken;

import java.lang.annotation.*;
import java.lang.reflect.AnnotatedParameterizedType;
import java.util.Arrays;
import java.util.List;

/**
 * @author zyc
 */
public class Test {

    @Target(ElementType.TYPE_USE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @interface Example {
    }

    public static void main(String[] args) {

        // Direct execution works
        AnnotatedParameterizedType annotatedParameterizedType = (AnnotatedParameterizedType) new TypeToken<List<@Example String>>() {
        }.getAnnotatedType();
        System.out.println(Arrays.toString(annotatedParameterizedType.getAnnotatedActualTypeArguments()[0].getDeclaredAnnotations()));

        // The static method works
        staticMethod();

        // The instance method does not work
        new Test().instanceMethod();
    }

    /**
     * static method
     */
    private static void staticMethod() {
        AnnotatedParameterizedType annotatedParameterizedType = (AnnotatedParameterizedType) new TypeToken<List<@Example String>>() {
        }.getAnnotatedType();
        System.out.println(Arrays.toString(annotatedParameterizedType.getAnnotatedActualTypeArguments()[0].getDeclaredAnnotations()));
    }

    /**
     * instance method
     */
    private void instanceMethod() {
        AnnotatedParameterizedType annotatedParameterizedType = (AnnotatedParameterizedType) new TypeToken<List<@Example String>>() {
        }.getAnnotatedType();
        System.out.println(Arrays.toString(annotatedParameterizedType.getAnnotatedActualTypeArguments()[0].getDeclaredAnnotations()));
    }

   @org.junit.Test
    public void test() {
        AnnotatedParameterizedType annotatedParameterizedType = (AnnotatedParameterizedType) new TypeToken<List<@Example String>>() {
        }.getAnnotatedType();
        System.out.println(Arrays.toString(annotatedParameterizedType.getAnnotatedActualTypeArguments()[0].getDeclaredAnnotations()));
    }
}

the expected result of the main method

[@com.zyc.Test$Example()]
[@com.zyc.Test$Example()]
[@com.zyc.Test$Example()]

the actual result of the main method

[@com.zyc.Test$Example()]
[@com.zyc.Test$Example()]
[]

junit result

[]

The test results are unexpected. Do you know where the problem is? thanks.

TypeVisitor has protected method that refers to package-private type

TypeVisitor has a protected visitCaptureType as if you are supposed to be able to override it.
However, the parameter is a AnnotatedCaptureType which is package-private, so to override it you must be in the geantyref package.

It seems that either the type should be visible, or the method itself should be package private not protected.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.