1

following code fails compiling the last line even though the second last one compiles ok while the difference is only in generic type that seems to be well within its bounds.

can anyone explain? I guess it relates to erasure but why the second-last statement compiles ok?

public class Main {
    public static class A {
        public class Inner {
        }
    }

    public static class B extends A {
        public class Inner {
        }
    }

    public static class Wrap<T extends A> {
        public Class<T.Inner> get(Class<T.Inner> cls) {
            return cls;
        }
    }

    public static void main(String... args) {
        Wrap<A> wa = new Wrap<>();
        Wrap<B> wb = new Wrap<>();
        // this compiles OK
        Class<A.Inner> ai = wa.get(A.Inner.class);
        // this fails
        Class<B.Inner> bi = wb.get(B.Inner.class);
    }
}

compiler error:

 /tmp$ javac -Xdiags:verbose Main.java
Main.java:24: error: method get in class Wrap<T> cannot be applied to given types;
        Class<B.Inner> bi = wb.get(B.Inner.class);
                              ^
  required: Class<Main.B.Inner>
  found: Class<Main.B.Inner>
  reason: argument mismatch; Class<Main.B.Inner> cannot be converted to Class<Main.B.Inner>
  where T is a type-variable:
    T extends Main.A declared in class Wrap
1 error
Antony
  • 53
  • 6
  • Does it compile when b doesn't extend a? – Josh Heaps Mar 11 '17 at 23:19
  • Possible duplicate of [Is List a subclass of List? Why aren't Java's generics implicitly polymorphic?](http://stackoverflow.com/questions/2745265/is-listdog-a-subclass-of-listanimal-why-arent-javas-generics-implicitly-p) – David Conrad Mar 11 '17 at 23:25
  • @JoshHeaps no, having the bounds as Wrap is the way to allow declaring generic return type that refers to T.Inner – Antony Mar 11 '17 at 23:31
  • @DavidConrad that referred topic is rather about Collections not being covariant. not applicable to my problem I suspect. – Antony Mar 11 '17 at 23:36
  • It's about generics not being covariant, it doesn't make any difference whether it's a Collection. To put it another way, the compiler has no idea that `Wrap` isn't some kind of collection (although, not one that implements `Collection`, obviously). – David Conrad Mar 11 '17 at 23:45
  • @DavidConrad thanks, my bad referring to collections. but still not sure my problem is generics not being covariant - ie `wb` is declared as `Wrap` and I am only trying to stuff it with its kind... – Antony Mar 12 '17 at 00:06
  • Sorry, you're right. I think instead it has something to do with A.Inner and B.Inner not having any relationship? Or at least, not one the compiler can fathom. – David Conrad Mar 12 '17 at 00:17

1 Answers1

3

I can't explain the required/found statements of java but when defining T as T extends A then T.Inner becomes A.Inner.

There are multiple possible changes to make it work:

  • remove Inner from B
  • define Inner of B as extends A.Inner and change the get method to public <C extends T.Inner> Class<C> get(Class<C> cls) (works using Eclipse compiler, does not work using JDK's javac)
  • define Inner of B as extends A.Inner and change the get method to public <C extends A.Inner> Class<C> get(Class<C> cls)

How it should be changed depends on what you want to achieve using this construct of classes.

Markus Benko
  • 1,507
  • 8
  • 8
  • my bad in the demo code - B.Inner should extend from A.Inner as per my real case... – Antony Mar 11 '17 at 23:46
  • In the error message, the required `Main.B.Inner` is the one that `B` inherits from `A`, but the found `Main.B.Inner` is the one that `B` declares itself. They just happen to have the same name. – Jorn Vernee Mar 11 '17 at 23:53
  • _when defining T as T extends A then T.Inner becomes A.Inner_ - right, I noticed that when dealing with this problem – Antony Mar 11 '17 at 23:54
  • @MarkusBenko: your second option still fails compiling the second statement: `required: Class found: Class reason: inference variable I has incompatible bounds equality constraints: B.Inner upper bounds: B.Inner where I,T are type-variables: I extends B.Inner declared in method get(Class) T extends A declared in class Wrap` – Antony Mar 11 '17 at 23:59
  • This unfortunately wouldn't be the first time that Eclipse's compiler behaves differently than Java's own. I will check later. – Markus Benko Mar 12 '17 at 04:49