8

I know that similar type inference questions have been posted before (Java compiler choosing wrong overload or Why does the Java 8 generic type inference pick this overload?) but I think we picked a more interesting case here.

public class TemplateMethod {

    public static void main(String[] args) {
        System.out.println(System.getProperty("java.version"));
        method("a", "b");
        method("a", 5);
        method("a", new B().get());
    }


    public static void method(String s, String cause) {
        System.out.println("String");        
    }

    public static void method(String s, Object parameters) {
        System.out.println("Object");
    }

    public static interface Base {
        String methodToImplement();
    }

    public static class Impl implements Base {
        public String methodToImplement() {
            return "Impl.methodToImplement";
        }
    }

    public static class B {
        public <T extends Base> T get() {
            return (T) new Impl();
        }
    }
}

The output is:

Exception in thread "main" java.lang.ClassCastException:
TemplateMethod$Impl cannot be cast to java.lang.String at
TemplateMethod.main(TemplateMethod.java:9)

What I cannot understand here, that if the return type is T extends Base how can String be candidate for the return type?

The previous questions on StackOverflow do not deal with the special case with generics like here so I think this is not a duplicate. At those cases mentioned above the Java8 compiler behaves as the language specification is written. In this case I think that is not the case, the signature method(String, String) should not need to be considered for the return type of T <T extends Base> since a String will never be a TemplateMethod.Base defined here.

Community
  • 1
  • 1
mpocsaji
  • 126
  • 5
  • 1
    Looks like a bug to me. Do you have the latest update? – Peter Lawrey Oct 20 '16 at 12:38
  • :o I stand corrected it seems – Hovercraft Full Of Eels Oct 20 '16 at 12:39
  • Replicated with JDK 1.8.0_45 and 1.8.0_102. Trying with latest... – Mena Oct 20 '16 at 12:41
  • Replicated with jdk 1.8.0_51 on ideone: http://ideone.com/ZwSZOw – Ivaylo Strandjev Oct 20 '16 at 12:43
  • I ran it on JKD 1.8.0_51 and it ran fine. Compiled and ran it under Eclipse Mars. – Buhake Sindi Oct 20 '16 at 12:43
  • Ran with 1.8.0_u60 and u102, both had the ClassCastException. – mpocsaji Oct 20 '16 at 12:59
  • Interesting, we have a working and an exception throwing version both with u51? Buhake on what platform did you succeed? – mpocsaji Oct 20 '16 at 13:03
  • @mpocsaji on Windows. Like I said, I compiled it and ran it using the IDE. – Buhake Sindi Oct 20 '16 at 13:05
  • Apart from editing the question (see last paragraph) how can I convince SO admins that this is not a duplicate? Am I the only one thinking this is a different question from http://stackoverflow.com/questions/30521974/why-does-the-java-8-generic-type-inference-pick-this-overload ? – mpocsaji Oct 20 '16 at 13:11
  • Thanks @BuhakeSindi - my platform is Linux. Trying to check with colleagues working on Windows platform as well. – mpocsaji Oct 20 '16 at 13:13
  • 1
    It is *exactly* the same issue as in the linked question or like [here](http://stackoverflow.com/a/28474319/2711488). You only have to face the fact that if your method `get()` promises to return whatever subtype of `Base` the caller wants, then the caller/compiler can say “Ok, the gimme `X extends String&Base`”. The fact that your method cannot hold the promise, is *your* problem, not the problem of the language or compiler. – Holger Oct 20 '16 at 13:31
  • @Holger, class String is final so there cannot be a class which `extends String implements Base`. This is a **fact** during compile-time as well. Hence, I still think that the signature `(String, String)` should be out of question during compile-time. – mpocsaji Oct 20 '16 at 13:54
  • 2
    Just because a class is `final` at compile time, it doesn’t imply that it has to be `final` at runtime. Just because a class doesn’t implement `Base` at compile time, it doesn’t imply that it won’t implement `Base` at runtime. Anyway, that’s not important. There are *rules*. And just because *you* think that something should be out of question, it doesn’t change the rules. The rule is, if your method promises to return `String&Base`, if the caller wishes, accept it, even if the promise can’t be held. Hey, your method is broken for *any* type other than `Impl`, `final` or not. – Holger Oct 20 '16 at 14:01
  • 2
    Other examples for surprising intersection types incorporating `final` classes are [here (`String&List`)](http://stackoverflow.com/q/38092697/2711488) and [here (`Integer&CharSequence`)](http://stackoverflow.com/q/36402646/2711488). – Holger Oct 20 '16 at 14:20

0 Answers0