2

This question may have some similar ones, but I hope you can look at the code structure here and provide some insight. I believe this either demonstrates a very subtle user error, or a very subtle unaddressed issue in the Java type system. This is a case in which Java struggles to infer the type of an argument passed into a method with the error message:

java: method process in class Usage cannot be applied to given types;
  required: OuterWrapper<C,InnerWrapper<C>>
  found: RealOuterWrapper
  reason: cannot infer type-variable(s) C
    (argument mismatch; RealOuterWrapper cannot be converted to OuterWrapper<C,InnerWrapper<C>>)

One interesting detail is that very similar code (listed at the bottom) does compile and run.

Usage.java:

public static <C extends Model> void process (OuterWrapper<C, InnerWrapper<C>> wrapper) {
    ;
}

public static void main(String[] args) {
    process(new RealOuterWrapper());
}

OuterWrapper.java:

public interface OuterWrapper <T extends Model, D extends InnerWrapper<T> >{}

RealOuterWrapper.java:

public class RealOuterWrapper implements OuterWrapper<RealModel, RealInnerWrapper>{}

InnerWrapper.java:

public interface InnerWrapper <T extends Model>{}

RealInnerWrapper.java:

public interface RealInnerWrapper extends InnerWrapper<RealModel>{}

Model.java:

public interface Model {}

RealModel.java:

public class RealModel implements Model {}

This code fails to compile. However, this code compiles successfully:


Usage.java:

public class Usage {
    public static <C extends Model> void process (Wrapper<C,C> wrapper) {
        ;
    }

    public static void main(String[] args) {
        process(new RealWrapper());
    }
}

Wrapper.java:

public interface Wrapper <A extends Model,B extends Model>{}

RealWrapper.java:

public class RealWrapper implements Wrapper<RealModel,RealModel>{}

Model.java:

public interface Model {}

RealModel.java:

public class RealModel implements Model {}

Finally, if this wall of text is too hard to reason about, here is my GitHub repo containing these files!


Edit: Rewriting RealInnerWrapper as a class that implements InnerWrapper, I added

Usage.java:

public static void main(String[] args) {
    process(new RealOuterWrapper());
    process2(new RealInnerWrapper());
}
public static void process2 (InnerWrapper<RealModel> wrapper) {
    ;
}

However, the latter method process2 gives no such error, which implies a RealInnerWrapper() is an InnerWrapper<RealModel>.

Aaron
  • 132
  • 10
  • 2
    The problem is that even though `RealInnerWrapper` extends `InnerWrapper`, that doesn't mean that an `OuterWrapper` is the same as an `OuterWrapper>`. This is very similar to the old `List` vs `List` question, which I'll now try to find. – Dawood ibn Kareem May 11 '22 at 23:10
  • OK, I believe this is a duplicate of [this question](https://stackoverflow.com/q/2745265) but I'm not going to vote, because my vote is a hammer. – Dawood ibn Kareem May 11 '22 at 23:11
  • @DawoodibnKareem To demonstrate in this case: add `void foo(D d);` to `OuterWrapper`, add `@Override public void foo(RealInnerWrapper d) { }` to `RealOuterWrapper`, then add `wrapper.foo(new InnerWrapper() {});` to `process`. Then if you force `main` to go through with `process((OuterWrapper>)(Object)new RealOuterWrapper());` [you get a `ClassCastException`](https://godbolt.org/z/7av4dxocd). Therefore the call to `process` without the cast is in fact unsafe. OP probably wants to add some wildcards somewhere... – HTNW May 11 '22 at 23:17
  • @DawoodibnKareem - the way I see the signature for `process` is as requiring an `OuterWrapper` with the parameters of some `Model` and an `InnerWrapper` wrapping a model of that same type. `RealOuterWrapper` takes a `RealModel` and a `RealInnerWrapper`, which wraps a `RealModel`. Am I misunderstanding the meaning of this type signature? Or is there some other detail at play? And I may close this as a duplicate, but I am curious about the semantics here first. – Aaron May 11 '22 at 23:35
  • And see my last edit, I would think that if `RealModel` is a `RealModel`, and `RealInnerWrapper` is an `InnerWrapper`, then why is `OuterWrapper` not an `OuterWrapper>`? Thank you! – Aaron May 11 '22 at 23:44
  • @Aaron Jon Skeet explains that really well in his answer to the question that I linked to in my second comment. – Dawood ibn Kareem May 11 '22 at 23:53
  • You could try something like `public static void process (OuterWrapper> wrapper)` - this may give you what you want. The point is that the `OuterWrapper` can't wrap things that are just `InnerWrapper`, which means that you mustn't go assigning one to a variable whose type is `OuterWrapper>` - which is fundamentally what you're trying to do. – Dawood ibn Kareem May 11 '22 at 23:57
  • So in that case, we are saying `?` = anything that extends `InnerWrapper`, or anything that extends `InnerWrapper`. Yep, I see the similarity between these questions now, although I am not convinced that the `?` should be required here, as that seems like the implied behavior. I will sit down and read this over a cup of coffee tomorrow morning and try to get my head in the place where this seems logical. Thanks @DawoodibnKareem. – Aaron May 12 '22 at 00:11
  • It certainly takes time for all this stuff to settle in the brain. To answer your question, `C` would get resolved to `RealModel`, so the effect is the same. My suggestion is to try it with the `?` and see if it makes the problem go away. Sometimes with these things, you end up with an error somewhere else when you make such a change. – Dawood ibn Kareem May 12 '22 at 00:20
  • Oh yeah - to be clear, that "fixed" it. I feel like that's just telling it to use anything that is descended from `InnerWrapper`, which I feel should be implicit always? As put by OP in the question you linked: "[Why do] I have to explicitly tell the method to accept a list of any subclass of Animal"? – Aaron May 12 '22 at 01:16
  • Oh! You're saying I can't just give it an `OuterWrapper` containing any arbitrary `InnerWrapper` and then reference that `InnerWrapper`, the `?` signifies a certain implementation of `InnerWrapper` which is used throughout? – Aaron May 12 '22 at 02:03

0 Answers0