`this` in concise method bodies

Brian Goetz brian.goetz at oracle.com
Fri Oct 12 17:15:53 UTC 2018


On 10/5/2018 7:22 PM, Dan Smith wrote:
> - It's not totally clear what we do with 'this'. Kind of seems like it 
> should be treated as the first parameter to be passed, but there are 
> also examples in the JEP that ignore it. And is it allowed to be 
> referenced by the receiver expression?
>
> void reverse() = Collections::reverse; // invoke 
> Collections.reverse(this)?
> int length(String s) = String::length; // invoke s.length()?
> void altMethod() = ThisClass::method; // invoke this.method()?
> void altMethod2() = this::method; // legal?

Drilling in on `this`:

One of the strongest motivating examples was:

     static final Comparator c = ...

     int compareTo(T other) = c::compare;

And in this case, we want this to wire to c.compare(this, other).

However, another strong motivating case is:

     final List myList = ...

     int size() = myList::size;

in which case we want this to wire to myList.size() and ignore `this` 
entirely.

And, both use cases are equally desirable.

We have some precedent for resolution that involves "try both", which is 
the Class::method syntax, where we'll match either an instance method or 
a static method passing the receiver as the first parameter.  However, 
this is a little different; in that case, we were still using all the 
parameters, just adapting them to paper over the somewhat accidental 
static/instance divide.  In this case, we want to drop the receiver when 
the wired-to method doesn't want it, which is a little messier.  But, 
also not totally novel; when adapting a value-returning method reference 
to a void-returning SAM, we are willing to drop the return value.

There are (at least) two ways we could attack this.  The first involves 
refining the "infer a target type from the method descriptor, and then 
use that for overload selection" intuition. Just as with Class::method, 
where we use the target type to do overload selection for both static 
and instance methods, and fail if we find neither or both, we can do the 
same thing; construct both the with-receiver and without-receiver SAM, 
and fail if there is not exactly one answer.

The other approach, which also leans on an existing precedent, is to 
start with the method reference; if it exact, use that to condition the 
adaptation to the implied SAM.  Which is slightly weaker that the first 
approach, as the exactness test can be fooled by inapplicable overloads 
(say, with completely wrong arity, or incompatible types.)

We already discourage overloads like:

     class C {
         static R x(C c, ARGS)
         R x(ARGS)
     }

because that will make method references C::x ambiguous.  With the 
approaches above, we'd also have ambiguities in cases like:

     class X {
         void m() = Bar::m;
     }

     class Bar {
         static void m(X x, ARGS) { }
         static void m(ARGS) { }
     }

That seems somewhat unlikely, but it becomes slightly more imagineable 
when you have something like:

     class X implements I {
         void m() = Bar::m;
     }

     class Bar {
         static void m(I i, ARGS) { }
         static void m(ARGS) { }
     }

But still, it doesn't seem like we'd be overrun with these.


Summary:

  - Both the capture-this and drop-this cases have important motivating 
use cases
  - Arbitrarily dropping one or the other would compromise the feature
  - There are some possibly reasonable ways of doing overload resolution 
and adaptation here, at some complexity.



-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.openjdk.java.net/pipermail/amber-spec-experts/attachments/20181012/cfcb8dff/attachment.html>


More information about the amber-spec-experts mailing list