14

Why does the below snippet compile ? OtherInterface does not extends Concrete so I would have bet a kidney that this wouldn't compile. But it does.

public class Test {

    public static interface SomeInterface {}

    public static interface OtherInterface{}

    public static class Concrete implements SomeInterface {

       public <T extends Concrete> T getConcrete() {
            return null;
       }
    }

    public static void doStuff() {
        Concrete c = new Concrete();
        OtherInterface iCompile = c.getConcrete();
    }
}

On the other hand, the next snippet does not compile, which is what I expect.

public class Test {

    public static interface SomeInterface {}

    public static class UnrelatedClass{}

    public static class Concrete implements SomeInterface {

       public <T extends Concrete> T getConcrete() {
            return null;
       }
    }

    public static void doStuff() {
        Concrete c = new Concrete();
        UnrelatedClass iCompile = c.getConcrete();
    }
}
Jerome
  • 2,104
  • 1
  • 17
  • 31
  • @azurefrog The second one doesn't compile for me (I'm not OP) and the error message is that he upperbound of `T` (which is `Concrete`) doesn't match with `UnrelatedClass`. – Tom Jul 03 '17 at 14:32
  • @azurefrog, Tom's comment is what I have – Jerome Jul 03 '17 at 14:33
  • 2
    The one that compiles is probably the same as in this question: https://stackoverflow.com/questions/29670018/why-can-this-generic-method-with-a-bound-return-any-type. The compiler infers an intersection type `Concrete & OtherInterface`. – Radiodef Jul 03 '17 at 14:33
  • Because `T` is inferred by the compiler to be ``. It sounds very counter-intuitive at first but it makes sense. – biziclop Jul 03 '17 at 14:35
  • @biziclop if you have more info about why this type inference occurs, I'm very insterested in it. That would actually be the exact answer I 'd like – Jerome Jul 03 '17 at 14:40
  • 1
    @Jerome My answer in the Q&A I linked to explains it. https://stackoverflow.com/a/29670591/2891664 – Radiodef Jul 03 '17 at 14:40
  • 3
    The point is that there _could_ be a class that extends `Concrete` and implements `OtherInterface`, and that theoretical type is what `T` is inferred to. – Jorn Vernee Jul 03 '17 at 14:43
  • I'll close this as a duplicate. Although I wonder the reasons leading to allow the first example to compile. It's true that the returned instance could be implementing `SomeInterface` but it's as much likely to not be, leading to potentially difficult to solve runtime errors. – Jerome Jul 03 '17 at 14:58
  • 2
    I think the secret sauce comes from returning null. Since you don't give any specific type to look at the compiler shrugs it's shoulders (metaphorically speaking) and kicks the problem further down field. I'm with Jorn Vernee - there _could_ be a class that extends `Concrete` and implements `OtherInterface`, and "null" doesn't give enough information for the compiler to argue with. – Chris Parker Jul 03 '17 at 15:07

1 Answers1

5

The difference is here:

public static interface OtherInterface{} ...
OtherInterface iCompile = c.getConcrete();

vs.

public static class UnrelatedClass{} ...
UnrelatedClass iCompile = c.getConcrete();

Meaning: in the first case, you call the method to return an instance of some interface. Interfaces can be any class.

In the second example, you instruct that returned type is of be a specific class! A class which is known, and that does not implement that other interface!

And the error message:

reason: no unique maximal instance exists for type variable T with upper bounds UnrelatedClass,Concrete

is pretty specific here.

In other words: the compiler factors in the left hand side of that assignment - to determine the valid types. And UnrelatedClass can never be a Concrete - because the class UnrelatedClass does not extend Concrete!

Whereas something that is SomeInterface can as well implement OtherInterface.

GhostCat
  • 137,827
  • 25
  • 176
  • 248
  • 1
    You're explaining why the second snippet does not compile, which is good, but I knew that already. It does not really explain why the first one does though. if `getConcrete()` was not generic and returned Concrete, the first snippet would not compile. – Jerome Jul 03 '17 at 14:35
  • 1
    Huh? The first example returns an instance of some interface. The actual class is unknown, and could well be implementing that other interface. – GhostCat Jul 03 '17 at 14:38