Java 8 javac wrong return type for a generic method
Maurizio Cimadamore
maurizio.cimadamore at oracle.com
Thu Nov 13 13:26:11 UTC 2014
This is a javac bug - when multiple applicable abstract methods are
found during overload, javac is supposed to return the one with the most
specific return type; unfortunately, when javac merges the two
signatures, it uses the 'instantiated' return type (String) instead of
the bare one (T). This leads to the missing checkcast.
I filed a new issue:
https://bugs.openjdk.java.net/browse/JDK-8064803
Thanks for the detailed report.
Maurizio
On 13/11/14 12:59, Mehmet Dogan wrote:
> Hi,
>
> I faced a weird compiler issue (or at least I looked weird to me)
> while compiling & running a unit test on Java8. It was running on Java
> 6 & 7 without a problem. Then I created a very basic sample to
> reproduce it locally.
>
> Test includes 3 interfaces, 1 implementation and 1 test class.
>
> /public interface ParentA<T> {/
> / T process() throws Exception;/
> /}/
> /
> /
> /public interface ParentB<T> {/
> / T process() throws Exception;/
> /}/
>
> /public interface Child<T> extends ParentA<T>, ParentB<T> {/
> /* // *//*everything works fine *//*when this line is uncommented */
> / // T process() throws Exception;/
> /}/
> /
> /
> /public class ChildImpl<T> implements Child<T> {/
> / @Override/
> / public T process() {/
> / return null;/
> / }/
> /}/
> /
> /
> /public class Test {/
> / public static void main(String[] args) throws Exception {/
> / Child<String> child = new ChildImpl<String>();/
> / String result = child.process();/
> / System.err.println(result);/
> / }/
> /}/
>
>
> 1 - When I compile these with javac 8 and run the main method in Test
> class, it fails with the following exception:
>
> /Exception in thread "main" java.lang.NoSuchMethodError:
> Child.process()Ljava/lang/String;/
> /at Test.main(Test.java:5)/
>
>
> 2 - When I uncomment overriding method definition in `/Child/`
> interface, then it works fine.
> /
> /
> /public interface Child<T> extends ParentA<T>, ParentB<T> {/
> / T process() throws Exception;/
> /}/
>
> 3 - When I remove `/throws Exception/` declaration from `/process/`
> method, then it works fine again.
>
>
> Then I looked at the disassembled bytecode of the Test.class and saw
> in Java 6 and 7 versions there is an additional `checkcast`
> instruction after `Child.process()` method call. See;
>
> - *With Java8:*
>
> /public class Test {/
> / public Test();/
> / Code:/
> / 0: aload_0/
> / 1: invokespecial #1 // Method
> java/lang/Object."<init>":()V/
> / 4: return/
> /
> /
> / public static void main(java.lang.String[]) throws
> java.lang.Exception;/
> / Code:/
> / 0: new #2 // class ChildImpl/
> / 3: dup/
> / 4: invokespecial #3 // Method
> ChildImpl."<init>":()V/
> / 7: astore_1/
> / 8: aload_1/
> /* 9: invokeinterface #4, 1 // InterfaceMethod
> Child.process:()Ljava/lang/String;*/
> / 14: astore_2/
> / 15: getstatic #5 // Field
> java/lang/System.err:Ljava/io/PrintStream;/
> / 18: aload_2/
> / 19: invokevirtual #6 // Method
> java/io/PrintStream.println:(Ljava/lang/String;)V/
> / 22: return/
> /}/
>
> *- With Java7:*
> /
> /
> /Compiled from "Test.java"/
> /public class Test {/
> / public Test();/
> / Code:/
> / 0: aload_0/
> / 1: invokespecial #1 // Method
> java/lang/Object."<init>":()V/
> / 4: return/
> /
> /
> / public static void main(java.lang.String[]) throws
> java.lang.Exception;/
> / Code:/
> / 0: new #2 // class ChildImpl/
> / 3: dup/
> / 4: invokespecial #3 // Method
> ChildImpl."<init>":()V/
> / 7: astore_1/
> / 8: aload_1/
> /* 9: invokeinterface #4, 1 // InterfaceMethod
> Child.process:()Ljava/lang/Object;*/
> /* 14: checkcast #5 // class java/lang/String*/
> / 17: astore_2/
> / 18: getstatic #6 // Field
> java/lang/System.err:Ljava/io/PrintStream;/
> / 21: aload_2/
> / 22: invokevirtual #7 // Method
> java/io/PrintStream.println:(Ljava/lang/String;)V/
> / 25: return/
> /}/
>
>
> Javac 8 is expecting to call a method with return type of String
> (/InterfaceMethod Child.process:()Ljava/lang/String)/, but on Java 7
> version is expecting a method with return type of Object
> (/InterfaceMethod Child.process:()Ljava/lang/Object/) which is
> expected because of type-erasure then it's checking type of returning
> value with `checkcast` instruction.
>
>
> Test environment:
>
> Centos 6.6 & OSX 10.10
>
> javac 1.8.0_25
> openjdk version "1.8.0_25"
> OpenJDK Runtime Environment (build 1.8.0_25-b17)
> OpenJDK 64-Bit Server VM (build 25.20-b23, mixed mode)
>
> javac 1.7.0_71
> java version "1.7.0_71"
> OpenJDK Runtime Environment (rhel-2.5.3.1.el6-x86_64 u71-b14)
> OpenJDK 64-Bit Server VM (build 24.65-b04, mixed mode)
>
> Is this an expected situation, is there something I'm
> understanding/using wrong? Or is this a bug/problem in Java 8 compiler?
>
> Thanks.
>
> *Mehmet Dogan
> *mehmet at hazelcast.com <mailto:mehmet at hazelcast.com>
> @mmdogan
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.openjdk.java.net/pipermail/compiler-dev/attachments/20141113/f465df4a/attachment.html>
More information about the compiler-dev
mailing list