[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…”
suggestions.
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