8306854: javac with '-source 7' should honor default methods in implemented Java 8 interfaces
Remi Forax
forax at univ-mlv.fr
Tue Apr 25 19:17:46 UTC 2023
----- Original Message -----
> From: "Alex Buckley" <alex.buckley at oracle.com>
> To: "compiler-dev" <compiler-dev at openjdk.java.net>
> Sent: Tuesday, April 25, 2023 7:09:41 PM
> Subject: Re: 8306854: javac with '-source 7' should honor default methods in implemented Java 8 interfaces
> On 4/25/2023 8:33 AM, Volker Simonis wrote:
>> ```
>> interface A {
>> void foo();
>> }
>> ```
>>
>> ```
>> interface B extends A {
>> @Override default void foo() { }
>> }
>> ```
>>
>> ```
>> class C implements B { }
>> ```
>>
>> If we compile `A.java` and `B.java` with `javac -source 8` and
>> `C.java` with `-source 7` we will get the following error:
>> ```
>> $ javac -source 8 A.java
>> $ javac -source 8 B.java
>> $ javac -source 7 C.java
>> C.java:1: error: C is not abstract and does not override abstract
>> method foo() in A
>> class C implements B { }
>> ```
>>
>> I think this is wrong, because `foo()` is implemented as a default
>> method in `B`.
>
> The Java language circa JLS7 does not have default methods, so the
> membership of C cannot possibly involve a default method inherited from
> B. The Java language is backward compatible, not forward compatible.
>
> A Java compiler that adheres to JLS7 (`-source 7`) is right to reject
> C.java -- either with the error given above, or with a more
> context-aware error along the lines of "C is written for Java 7, cannot
> inherit default method from B".
>
>> The following, slightly simpler example works perfectly fine, although
>> it also depends on a default method in an implemented interface:
>>
>> ```
>> interface D {
>> default void bar() { }
>> }
>> ```
>>
>> ```
>> class E implements D { }
>> ```
>>
>> ```
>> $ javac -source 8 D.java
>> $ javac -source 7 E.java
>> ```
>>
>> In the second example, `javac` happily accepts the default
>> implementation of `bar()` from interface `D`.
>
> It's hard to see how javac is processing class E, so I added a method
> declaration `@Override public void bar() {}` to E and `javac -source 7
> E.java` accepted the code. That's improper, because in Java 7 there is
> no such thing as a default method in D to override from E.
>
> I then added a method invocation `D.super.bar();` as the body of E's bar
> method. In JLS8, this means "invoke my superinterface's default method".
> However, in JLS7, it means "invoke my enclosing class's method", but D
> isn't an enclosing class, so it's illegal (see
> https://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.12.1-100-D)
> -- yet `javac -source 7 E.java` accepts it anyway. Highly improper on
> javac's part.
>
> Alex
Alex, Java 8 adds a lot of default methods to the interfaces of the collection API, so javac8 -source 7 could not stop each time it sees a default methods, otherwise a lot of code will never have compiled. Which means that javac7 -source 7 and javac8 -source 7 have to have different behaviors.
It was decided that javac8 -source 7 should skip default methods because it works well with the collection API even if it does not work that well in other cases as the one provided by Volker.
Volker, I believe the behavior you see is not a bug, it's how javac should behave. But i don't think this behavior is specified somewhere, that why ecj has a different behavior. Moreover, javac never behaves differently depending on a specific classfile version of a specific classfile to preserve the sanity of the people trying to debug issues involving several versions.
regards,
Rémi
More information about the compiler-dev
mailing list