8306854: javac with '-source 7' should honor default methods in implemented Java 8 interfaces

Volker Simonis volker.simonis at gmail.com
Wed Apr 26 12:33:05 UTC 2023


On Tue, Apr 25, 2023 at 10:03 PM Joseph D. Darcy <joe.darcy at oracle.com> wrote:
>
> On a related point, among the many advantages of using
>
>      javac --release $OLD ...
>
> over something like
>
>      javac -source $OLD
>
> is that whole categories of version mismatches, how newer language
> structures should be presented to older source versions, are avoided
> with respect to the platform libraries.
>

In general I agree, but "--release" is not available for JDK 8 and as
you've mentioned, it only works for the platform libraries but not for
third party dependencies. Finally, it elegantly avoids the problem of
how to "present newer language structures to older source versions" by
not using class files at all but instead relying on purely symbolic
information from the ct.sym file (but again, only for the platform
classes).

> Compiling clients of libraries at -source/--release level no earlier
> than their dependencies seems like a reasonable build configuration
> constraint.

Again, I agree in general, but imagine that your client (and not the
dependent library) has to be processed by an old library which doesn't
understand the newer bytecode version.

>
> -Joe
>
> On 4/25/2023 12:17 PM, Remi Forax wrote:
> > ----- 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