xvik / generics-resolver

Java generics runtime resolver
https://xvik.github.io/generics-resolver
MIT License
45 stars 9 forks source link

"Generic is not declared" when generic parameters aren't declared in order #3

Closed Namnodorel closed 5 years ago

Namnodorel commented 6 years ago

Maybe I'm just missing something (I'm just trying to learn this lib), but thsi seems like a bug to me. I have the following classes:

private abstract class MyAbstractClass<T extends List<? extends D>, D>{
    public abstract D myMethod(T myObject);
}

private class MyConcreteClass extends MyAbstractClass<List<String>, String>{
    @Override
    public String myMethod(List<String> myObject) {
        return null;
    }
}

Now, what I wanted to do was check whether some runtime object can be used as parameter for myMethod(). But the first line already fails:

    GenericsContext context = GenericsResolver.resolve(MyAbstractClass.class)
            .type(MyConcreteClass.class);

This throws an UnknownGenericException:

ru.vyarus.java.generics.resolver.error.UnknownGenericException: Generic 'D' (defined on     MyAbstractClass<T, D>) is not declared 

at ru.vyarus.java.generics.resolver.util.GenericsUtils.declaredGeneric(GenericsUtils.java:480)
at ru.vyarus.java.generics.resolver.util.GenericsUtils.resolveTypeVariables(GenericsUtils.java:222)
at ru.vyarus.java.generics.resolver.util.GenericsUtils.resolveTypeVariables(GenericsUtils.java:268)
at ru.vyarus.java.generics.resolver.util.GenericsUtils.resolveTypeVariables(GenericsUtils.java:247)
at ru.vyarus.java.generics.resolver.util.GenericsUtils.resolveTypeVariables(GenericsUtils.java:268)
at ru.vyarus.java.generics.resolver.util.GenericsUtils.resolveTypeVariables(GenericsUtils.java:231)
at ru.vyarus.java.generics.resolver.util.GenericsResolutionUtils.resolveRawGeneric(GenericsResolutionUtils.java:193)
at ru.vyarus.java.generics.resolver.util.GenericsResolutionUtils.resolveDirectRawGenerics(GenericsResolutionUtils.java:144)
at ru.vyarus.java.generics.resolver.util.GenericsResolutionUtils.resolveRawGenerics(GenericsResolutionUtils.java:125)
at ru.vyarus.java.generics.resolver.util.GenericInfoUtils.create(GenericInfoUtils.java:42)
at ru.vyarus.java.generics.resolver.context.GenericsInfoFactory.create(GenericsInfoFactory.java:56)
at ru.vyarus.java.generics.resolver.GenericsResolver.resolve(GenericsResolver.java:34)
    [my code trace]

It appears that generics-resolver does not recognize D as valid generic parameter, because it is declared later on. But it is valid in Java to declare and use Generic parameters in any order, so I think it should be prepared for that.

xvik commented 6 years ago

Oh, yes that's a bug - I indeed did not expect such order (strange, I was sure I chacked that it is impossible). Change declaration order, if you can, and it will work. I will need some time (which I temporary don't have) to release a fix.

GenericsContext context = GenericsResolver.resolve(MyAbstractClass.class) .type(MyConcreteClass.class);

This usage is not correct: you should resolve hierarchy from the top class and only then you can navigate to containing classes:

GenericsResolver.resolve(MyConcreteClass.class).type(MyAbstractClass.class)

If you want to navigate to some method, you don't need to manually change type before it (.type(MyAbstractClass.class)) - simply use .method(methodInstance) and it will use correct type automatically.

Namnodorel commented 6 years ago

Wow, that is a fast response :D My real world example is a little more complex, and although that sounds weird, that order was more readable. But I'll change the order until the fix is here. Thank you for the tip on usage, I would've had problems with that pretty soon.

Keep up the good work! ^^

Namnodorel commented 6 years ago

Whoa, hold on - when I resolve MyConcreteClass.class first and the use .type(MyAbstractClass.class), the exception is not thrown. The error still should probably be a little different, but maybe the bug only occours as a consequence of wrong usage? Maybe then it could simply be fixed by checking the given arguments.

xvik commented 6 years ago

There are few places where order indeed matter, so it's still a bug. Now you go by different logic branch, and I'm not sure why it works (it could simply replace D by Object, or may work correctly.. need to look).

Maybe then it could simply be fixed by checking the given arguments.

No) Context building (GenericsResolver.resolve(MyConcreteClass.class)) requires only one argument and you may use any class there. In your first case, it was context resolution from the abstract class and so solving its generics by upper bound (declaration). In the second case, resolution goes from the top class which has declared generics for the abstract class.

.type() is just navigation by classes in resolved class hierarchy.