[External] : Re: Method and Field Literals

John Rose john.r.rose at oracle.com
Fri Apr 29 20:01:06 UTC 2022

On 24 Apr 2022, at 8:31, Brian Goetz wrote:

> So, a feature where field and method literals evaluated only to Method 
> objects is a pretty weak one, and worse, fi the goal is merely to pull 
> the reflection API into the language, is likely to result in requests 
> to pull further.  Though I do enjoy the extra type checking that 
> IntelliJ gives me for method and method handle lookup.)
> The reality is that Method is not a very useful type; reflection is an 
> escape hatch, which has more benefit for frameworks than for ordinary 
> user code.  You might say “but we have class literals”, but this 
> is mostly a “for consistency” argument*, and one that doesn’t 
> even survive much scrutiny.
> Class literals have two aspects that method literals would not: their 
> role in inference (Foo.class has type Class<Foo>, meaning that it can 
> be used as a source of constraints in type inference), and the 
> Object::getClass method.  Method and field literals would have neither 
> of these two touch points.  (If it were not for the type inference 
> aspect, if we didn’t have class literals now, I might not be 
> inclined to add them — Class::forName works  too.)  So I would rate 
> this a pretty weak feature.
> The main reason method references are useful and these are less useful 
> is that methods have behavior, and method refs allow you to access 
> that behavior through the suitable abstraction level (e.g., Function, 
> Supplier.)  Passing around Method or MethodHandle or VarHandle is not 
> particularly desirable for ordinary application code.
> There’s a version of method (and field) references that is more 
> interesting, where we can use target typing to let Foo::bar evaluate 
> to a SAM type, Method, MethodHandle, MethodDesc, or other description 
> of a method.  (Note that you also have to pull in additional syntax 
> and behavior to support overload selection, since we don’t have a 
> SAM type to guide the overload selection.)  That’s more interesting, 
> but a more complex feature.  Fields are even more complicated, because 
> fields have TWO behaviors, and the obvious representations (Field, 
> VarHandle) are also pretty low-level and unsuitable to 
> application-level interaction.  (If the language had Properties, then 
> turning a field into a Property would be a more appropriate API-level 
> encoding.)
> *If the best argument you can make for a feature is “for 
> consistency”, that’s usually a sign that the feature is weak.

This is a very good summary for the public record of the core design 
issues for method (and field) literals.  I think target typing is a 
promising tactic here but there are lots of fiddly details to work out.  
It’s definitely not one of those a “why don’t you just…” 

I have two minor comments to add:

1. Brian mentions that target-typing to M/MH/MD fails to select 
overloads.  Some of that could be worked around (w/o syntax changes) by 
encouraging users to convert first to a FI object (which contains a 
method signature) and then to the desired M/MH/MD.  That would require 
fiddly rules to ensure that the right answer came out of the cascaded 
conversions, instead of an opaque FI object wrapper, so maybe an overt 
signature annotation syntax would be better on balance.  (The opaque 
wrapper is harmless for MHs but not jlr.Ms or MDs.)

2. Brian mentions that fields have two behaviors.  Actually they can 
have a bundle of many behaviors, including atomics, *when viewed through 
the VH lens*.  And that provides a way to think about those multiple 
behaviors:  Allow a field ref to be target-typed to a VH, and you get 
all the behaviors.  (Similar point for jlr.F and FD.)  I think it would 
be reasonable, for MHs and FIs, to simply return the read behavior and 
ignore the other behaviors.  After all, read is the most important 
operation on a field (and the only one, for a final field).

An aside:  If you really want to have static checking of name strings, 
you can do this today by building a private-static-final constant 
containing the string, as extracted (in the static initializer) from an 
appropriately bugged serializable method reference (maybe using a helper 
function).  Yeah, it’s harder than syntax sugar, but it can be done.

My take on the priority of better method and field literals:  I’m 
generally supportive of such a feature, for ease of use, for better 
type-checking, also for efficiency (ldc/condy), and with the end result 
of making practical more kinds of self-reflecting code (using 
MH/VH/MD/FD as well as good old jlr).  *That said,* it’s definitely an 
expert feature, so it doesn’t deserve a large footprint in the JLS.  
And, it has to be done right (for a range of target types), to justify 
any footprint in the JLS.  A full solution of those goals and 
constraints is yet to be designed, and will require a concerted effort 
at some point.  But, we are working on higher value JLS features these 
days, for everybody (not just framework experts).

I also suspect that a discussion of method and field literals will 
eventually be better informed by a deep consideration of other kinds of 
literals, starting with list/map/set and also validated strings and 
maybe quoted expressions.  (These would benefit all programmers not just 
experts.)  All user-defined literals would surely benefit from target 
typing, so maybe the considerations of TT for M/MH/etc. are just a 
special case of something more general.  So I will speculate that method 
and field literals could possibly end up as a feature that is derived, 
partially or wholly via library logic, from a more fundamental form of 
programmable literal.  (Static checking could be handled by compile-time 
intrinsic execution, for which there also are designs on the back 
burner.)  But, again, we are not ready to put such features at the top 
of the priority queue.

More information about the discuss mailing list