Virtual extension methods -- a strawman design

Reinier Zwitserloot reinier at zwitserloot.com
Tue May 25 10:15:11 PDT 2010


Alex, replies inline.

--Reinier Zwitserloot

On Sun, May 23, 2010 at 5:03 PM, Alex Blewitt <alex.blewitt at gmail.com>wrote:

>
> However, I have concerns with the defender methods in more general terms.
> Putting an alias to a static method is akin to permitting code in an
> interface; what happens in the following case:
>
>
[SNIP: Diamond problem]

I thought the spec was pretty clear. If compiling the leaf of a diamond
situation (class AB implements A, B), then javac will refuse to generate a
class file and will instead error out with a message stating something along
the lines of: "method stuff() has default implementations in both A and B.
Resolve this ambiguity by writing your own stuff() method."

If the JVM runtime environment consists of mixed classes, then the diamond
problem can still occur without anyone getting a compiler error. In this
case, the usual thing happens when you run a JVM environment with mixed
classes: Errors. I don't see why this proposal needs to be held to a higher
standard here. The key point is that errors _do_ occur, which is a lot
better than silently picking one of the multiple available default
implementations (better because by picking one, order becomes relevant, and
that's bad). As was mentioned in the spec, the compiler will automatically
insert an implementation of the stuff() method in any class implementing
either A or B (or both), wrapping the call to the provided default. This
mechanism can detect the diamond situation just as easily, and can then
generate a method that will throw an exception, in keeping with how the JVM
behaves with interfaces in general (no load errors, instead you get some
sort of throwable when you call a method). All that needs to be ensured is
that the situations where such a diamond situation would indeed occur is
rare (and it is trivial to see that it will be). I would agree with you that
the situation would have to be excluded entirely if the JVM would have to
resort to ambiguous behaviour when it happens, but that's not the case here.


>
> Even without that, I don't see that any consideration has been given to
> what happens to already existing methods which are used prior to this
> change. For example, consider the lowly 'filter' method - some
> implementations may perform an in-place filter, returning 'true' or 'false'
> to indicate if any changes were allowed, whereas others may return a new
> copy of the data structure with elements that match the filter. What if a
> generic 'filter' method is then defaulted to the interface?
>
>
Again a problem java already suffers from. You can add filter() to List
right now and it'll cause one of two things to happen if some list
implementation already has a filter method:

(1) The signature matches _exactly_. In this case, the filter() of the
ListImpl will override the interface's filter method. Awkward situation if
ListImpl's filter has different semantics from those explained in List's
filter().

(2) The signatures do not match _exactly_. In this case, the filter() of the
ListImpl doesn't override anything, and the usual thing happens:
NoSuchMethodError.

If (re)compiling ListImpl, you'll get a warning on filter() due to a missing
@Override, presuming you've got your build system set up properly.


There's a convoluted way out of this, but I doubt it's worth it. From javac7
and up, *ALL* methods are compiled with an extended field in the class file
listing all methods it was meant to override (this field is still present if
you're not overriding anything). When doing virtual lookup, methods are
ignored if there is no chain towards the target method in this extended
field. If the field is missing (compiled with javac6 or less), for backwards
compatibility reasons, the method is presumed to override everything.

Now we have a situation where previously there was no common filter()
> signature, there is now. The problem is that class A may well have been
> written long before the existence of B, but we have now introduced a
> situation where client code invoking A.filter() is presented with the return
> value of a different type to which it expects.
>
>
That won't happen. Unless a signature matches _exactly_ it's not the same
method. Therefore anyone invoking any method will always get back a value of
exactly the type they were expecting.


> The key problem is that the draft spec has no observations of what should
> happen when the extension method is of a different signature (i.e. not
> covariant in the return types).
>
>
As it was primarily focused on the JVM/class file, there was no need. You
can't have overriding of methods if signatures are different. Even covariant
return types don't exist at the JVM level; javac instead generates a bridge
method. At the javac level, this situation can be detected, and an error can
be produced.


More information about the lambda-dev mailing list