FasterXML / java-classmate

Library for introspecting generic type information of types, member/static methods, fields. Especially useful for POJO/Bean introspection.
http://fasterxml.com
Apache License 2.0
258 stars 42 forks source link

Parametrised classes does not resolve method types properly #28

Closed dawrut closed 9 years ago

dawrut commented 9 years ago

There is an issue when method's argument is bounded to a class type. As below, the foo method's parameter is resolved to: java.lang.Number Whatmore, the type has a resolved constructor, even it is an abstract class.

Full output:

java.lang.Number<Object>
java.lang.Number

Code snippet to reproduce:

      public class App {
          public static void main(String[] args) {
              TypeResolver resolver = new TypeResolver();
              ResolvedType resolvedType = resolver.resolve(A.class);
              MemberResolver memberResolver = new MemberResolver(resolver);
              ResolvedTypeWithMembers resolvedTypeWithMembers = memberResolver.resolve(resolvedType, null, null);

              for (ResolvedMethod method : resolvedTypeWithMembers.getMemberMethods()) {
                  for (int i = 0; i < method.getArgumentCount(); i++) {
                      ResolvedType argumentType = method.getArgumentType(i);
                      System.out.println(argumentType);
                      argumentType.getConstructors().forEach(System.out::println);
                  }
              }

          }
      }

      class A<T extends Number> {
          public void foo(T t) {
          }
      }
cowtowncoder commented 9 years ago

I am not sure what the problem here is: resolved type for method parameter t for foo should be java.lang.Number; ClassMate uses the bound as base in resolving type parameters. What type would you expect here?

As to abstract classes, they can (and often do) have constructors for sub-classes to call. So if you do not define any constructors, the standard Java language rules will lead to inclusion of implicit public zero-argument constructor. That would happen in case above.

dawrut commented 9 years ago

You are right about the constructors, my bad. I haven't noticed that GitHub didn't include type parameter for first output. The program produces: java.lang.Number<Object> whereas Number is not parametrised. I've updated the first entry to include the type parameter in the output.

Thanks

cowtowncoder commented 9 years ago

Hmmh. That parameterization looks odd, as Number is not a generic type. And also that two types are listed, suggesting two arguments?

dawrut commented 9 years ago

Exactly, the parametrised type is not a proper computation - that's the issue.

The second output is result of printing resolved constructors, so please ignore as I was wrong about it. It is output of: argumentType.getConstructors().forEach(System.out::println)

I attach a screenshot of debugging below where the type is assigned to the java.lang.Object. You can find some additional information like variables' values, could be useful.

screen shot 2015-06-04 at 10 48 32

Thanks

dawrut commented 9 years ago

I've dug a bit and I can see that bindings are passed as screenshots states below:

screen shot 2015-06-04 at 10 58 37

The easiest way to fix that is put there a condition which would check the predicate if a raw type has parameterised types - if so, then assign type bindings. I haven't analysed the resolving algorithm to trace the issue, so this fix could be... a workaround? I think, it would be better to check for that, before algorithm starts resolving the bindings.

screen shot 2015-06-04 at 11 02 29

cowtowncoder commented 9 years ago

Perhaps I better start with the original code as a test case and see what I can find. Thank you for debuggin help so far!

cowtowncoder commented 9 years ago

I added a unit test that verifies the issue, failing. Hope to figure out why that type parameter lingers.

Thank you for reporting the problem!

cowtowncoder commented 9 years ago

Also seems like this could be related to #26 you reported earlier.

dawrut commented 9 years ago

Yes, this is true. As I can see the #26 is similar issue I'd spot before, but more complex. My first guess to resolve this issue is to check whether a type to be resolved has any type parameters in a raw type (as in third screenshot shows a length is equal to 0).

Many thanks for a quick response!

cowtowncoder commented 9 years ago

Yes, you may be on right track there. I hope to figure this out in near future, but at the same time it is hard to estimate when that is. So I am open to fix suggestions, and can verify if they are valid. Actually unit test suite should be reasonable guard against regressions as well, wrt side effects of changes.

cowtowncoder commented 9 years ago

Looks like this was due to placeholder (needed to avoid problems with some self-references) being also exposed as "regular" type parameter. I separated usages to hide temporary binding (of variable name), and now tests pass as expected.