JEP 401 -- reflection and class literals
Gernot Neppert
mcnepp02 at googlemail.com
Thu Jun 24 07:16:41 UTC 2021
I'm reading your various proposals to reconcile descriptors with
type-mirrors, and they all seem to have their awkward corner-cases.
Granted, we have "Q-type" descriptor vs. "L-type" descriptors for class
members, method argumens and method return-types, and we need to make
this disctinction visible *somehow* via reflection.
But: do we really need to have two different type-mirrors corresponding
to these descriptors?
Isn't the "Q-ness" rather a property of the Field, method Parameter or
method return-type, and therefore should be available there via
reflection as a simple boolean property?
Given a value-favoring class "Point" and the following declaration:
public class Foo {
Point.ref pt;
public void setPoint(Point p) {
this.pt = pt;
}
public Point.ref getPoint() {
return pt;
}
}
Extending the reflection API for the corresponding classes from
java.lang.reflect would look something like this:
assertFalse(Point.class.getDeclaredField("pt").isByValue());
assertTrue(Point.class.getMethod("setPoint",
Point.class).getParameters()[0].isByValue());
assertFalse(Point.class.getMethod("getPoint").getParameters()[0].isReturnByValue());
Am 23.06.2021 um 18:44 schrieb Dan Smith:
>> On Jun 23, 2021, at 9:13 AM, Brian Goetz <brian.goetz at oracle.com> wrote:
>>
>> - In the context of a variable type, Point is an alias for Point.val, or Point.ref, if ref-default (no change from current);
>> - Where a class is needed (e.g., new Point(), instanceof Point), Point refers to the class Point, not any of its derived types, and Point.{ref,val} are not valid in these contexts (no change from current);
>> - Where a reflective literal is needed, the unqualified Point.class is always the primary mirror, which always matches the behavior of Object::getClass (change);
>> - If an explicit literal is needed for reflection (e.g., calling getMethod()), you can be explicit and say Point.{ref,val}.class to say what you mean (no change).
>>
>> This aligns the meaning of "Point.class" with the actual behavior of other reflective API points.
> I find that all solutions in this space tend to be bad in one way or another; this one seems as good as, or better than, most.
>
> Where it is weakest is when someone wants to talk about both classes and types in the same vicinity, and would rather not think through the subtle distinctions. Example:
>
> primitive record Point(int x, int y) {
> double distance(Point p2) { ... }
> }
>
> assert new Point(1, 2).getClass() == Point.class; // good
> assert Point.class.getMethod("distance", Point.class) != null; // not good, have to say Point.val.class
>
> We can "fix" this behavior by supporting "fuzzy matching" in the 'getMethod' method, so that both Point.val.class and Point.ref.class are considered matches for Point.val.class in method signatures. That feels to me like a bridge too far in our efforts to hide complexity from API users. YMMV. (Also doesn't work great if the language supports val/ref overloads; I think we lean towards *not* supporting these.)
>
> ---
>
> For completeness, here are a couple of other solutions we talked about, both of which are plausible, but we haven't enthusiastically embraced them:
>
> 1) Orient reflection more towards the language by tying 'getClass' to the ref-default flag. So the "primary mirror" is the L type for ref-default primitive classes, and the Q type for others. 'getClass' always returns the primary mirror, so always returns Foo.class (where 'Foo.class' always means the same thing as the type 'Foo').
>
> Big problem here is tying such core runtime behavior to the ref-default flag, which we've envisioned as a pure compile-time name resolution feature. JVM internals may be uncomfortable with this definition of "primary mirror", depending on how closely they are tied to java.lang.Class.
>
> 2) Keep pulling on the thread of "people just want to say Foo.class" by backing off of the "one java.lang.Class per descriptor type" invariant. Instead, there is just one Point.class; that's what you get from 'getClass()', that's what you see when you query Fields for their type (whether it's Point.val or Point.ref), that's what you use to match 'getMethod'. APIs that need more precise expressiveness (MethodHandle) should use a different abstraction.
>
> This gets us out of the problem of wanting most users to pretend that Point.val.class and Point.ref.class are the same, except where the seams are exposed (like ==). Biggest problem here is that changing something like MethodHandle to no longer build on java.lang.Class is a significant change, with tentacles in the JVM.
>
More information about the valhalla-spec-observers
mailing list