Default interface methods aren't considered abstract method implementations

Joseph D. Darcy joe.darcy at oracle.com
Tue Feb 11 21:56:58 UTC 2025


Hi Attila,

FYI, some of the documentation for Project Lambda

https://openjdk.org/projects/lambda/

hints at the general design rationale here. For example, the default 
methods (nee "defender methods") doc [1] discusses "favor classes over 
interfaces" in method resolution and "if multiple methods from 
interfaces, the user has to choose."

HTH,

-Joe

[1] https://cr.openjdk.org/~briangoetz/lambda/Defender%20Methods%20v4.pdf

On 2/10/2025 1:44 AM, Attila Szegedi wrote:
> Hi both Chen and Pavel,
>
> Thank you both for answering! I was not specifically asking Brian, of 
> course, that’s why there’s a wink next to it :-)
> I now scoured JLS 23, specifically:
>
> ----
> 8.1.1.1. abstract Classes
>
> A class C has abstract methods if either of the following is true:
>
> Any of the member methods (§8.2) of C - either declared or inherited - 
> is abstract.
>
> Any of C's superclasses has an abstract method declared with package 
> access, and there exists no method that overrides the abstract method 
> from C or from a superclass of C.
>
> 8.2. Class Members
>
> The members of a class are all of the following:
>
> Members inherited from its direct superclass type (§8.1.4), except in 
> the class Object, which has no direct superclass type
>
> Members inherited from any direct superinterface types (§8.1.5)
>
> Members declared in the body of the class (§8.1.7)
>
> 8.1.5. Superinterfaces:
>
> Each default method (§9.4.3) of a superinterface of the class may 
> optionally be overridden by a method in the class; if not, the default 
> method is typically inherited and its behavior is as specified by its 
> default body.
> -----
>
> And nothing in there tells me unambiguously that my example should’t 
> work (the last quoted sentence from 8.1.1.1 is weird, but adding 
> “public” to declaration of “m” circumvents it and it still doesn’t 
> compile.) There are few phrases that seem open to interpretation 
> though: "default method is _typically_ inherited” (typically is doing 
> heavy lifting here: sometimes it isn’t?) and “_any of the member 
> methods_ (§8.2) of C - either declared or inherited - is abstract" 
> (are A.m() and F.m() treated as separate member methods at this stage 
> despite identical signatures?)
>
> Again, I’m not arguing current javac behavior is wrong, just trying to 
> understand why is it right. I actually hit this in an IRL refactoring 
> – had several classes that both extended an abstract class (A) and 
> implemented an interface (F), and all these classes had the same 
> implementation for an abstract method (m) that was actually 
> semantically related to F and it felt like a good example of DRY to 
> implement the method only once as a default in F. (Yes, I could create 
> an interim class AF and move the implementation there, but this felt 
> like it would’ve been a good use of an interface as a mixin.)
>
> I wonder if I recreated the example in bytecode directly whether the 
> JVM would load the class successfully (I might need to attempt to also 
> call the method) or would I get some kind of IncompatibleClassChangeError.
>
> Attila.
>
> On 2025. Feb 10. at 1:58:32, Chen Liang <liangchenblue at gmail.com> wrote:
>> Hello guys, amber is for new language features. Questions with java 
>> compilation or JLS/JVMS belong to the compiler group.
>>
>> That said, I believe this behavior is most likely a preservation of 
>> method resolution, that for instance methods, methods from superclass 
>> have higher priority than methods from interfaces. For example, 
>> before Java 8, static methods could not be defined in interfaces; and 
>> when they were added in 8, resolution rule states that they aren't 
>> shadowed to child types, so classes cannot "inherit" interface static 
>> methods, unlike interface static fields.
>>
>> Another fun fact is that superinterface fields take precedence in 
>> resolution over superclass fields. Also a legacy from old Java, but I 
>> don't think that is going to be changed to make things "consistent".
>>
>> Regards,
>> Chen Liang
>>
>> On Sun, Feb 9, 2025 at 4:26 PM Pavel Rappo <pavel.rappo at gmail.com> wrote:
>>
>>     Attila,
>>
>>     Come to think of it, we should use some other, more narrowly focused,
>>     mailing list. Apologies to subscribers of this list. Since lambda-dev
>>     is defunct now, perhaps we could use compiler-dev, amber-dev, or
>>     core-libs-dev instead. I'm voting for amber-dev, where Brian mostly
>>     resides these days.
>>
>>     -Pavel
>>
>>     On Sun, Feb 9, 2025 at 10:12 PM Pavel Rappo
>>     <pavel.rappo at gmail.com> wrote:
>>     >
>>     > You are not specifically asking Brian, are you? If so, sorry
>>     for replying.
>>     >
>>     > My humble take is that the default method is something to use when
>>     > there's nothing else there. When there's something, even if it's
>>     > abstract, we use that. So, a default method is a minimally
>>     interfering
>>     > fallback/extension scenario, if you will.
>>     >
>>     > That abstract A.m() effectively replaces default F.m() in C as if
>>     > F.m() were never there. If F.m() were allowed to provide
>>     > implementation for A.m() in C, then some uncompliable code could
>>     > suddenly become compilable and operational at runtime.
>>     >
>>     > -Pavel.
>>     >
>>     > On Sun, Feb 9, 2025 at 9:04 PM Attila Szegedi
>>     <szegedia at gmail.com> wrote:
>>     > >
>>     > > Hey folks,
>>     > >
>>     > > I found a somewhat puzzling behavior: default interface
>>     methods aren't considered abstract method implementations. Trying
>>     to compile this minimal reproducer:
>>     > >
>>     > > ------ C.java ------
>>     > > abstract class A {
>>     > >   abstract void m();
>>     > > }
>>     > >
>>     > > interface F {
>>     > >   default void m() {}
>>     > > }
>>     > >
>>     > > class C extends A implements F {}
>>     > > ------ C.java ------
>>     > >
>>     > > $ javac C.java
>>     > > C.java:9: error: C is not abstract and does not override
>>     abstract method m() in A
>>     > > class C extends A implements F {
>>     > > ^
>>     > > 1 error
>>     > >
>>     > > I can accept this being valid according to JLS today (I tried
>>     with 11, 21, and 23.) I admit this is one of rare occasions when
>>     I didn't go scouring the JLS to figure out what exactly prevents
>>     the compiler from accepting F.m() as implementation of A.m() in
>>     C. I'm wondering though if this isn't something that could be
>>     improved upon in the future. (I'm sure there's a gotcha
>>     somewhere. Right, Brian? ;-) )
>>     > >
>>     > > Have a great day,
>>     > >   Attila.
>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/compiler-dev/attachments/20250211/b793626d/attachment-0001.htm>


More information about the compiler-dev mailing list