> From: "Brian Goetz" <brian.go...@oracle.com> > To: "valhalla-spec-experts" <valhalla-spec-experts@openjdk.java.net> > Sent: Mardi 29 Juin 2021 18:48:25 > Subject: Re: JEP 401 -- reflection and class literals
> The general consensus here is that this stacking is slightly better than the > previous one, so let's take this as the plan of record. Now, to explore the > next turn of the crank... > The concerns that were raised could be characterized by "do we *really* need > to > even have separate class literals for Foo.ref and Foo.val?" And the answer is, > sort of yes, sort of no, so there is a possible next turn of the crank that > might or might not be a further improvement. > Two existing constraints we have are: > - Reflection (and MethodHandle) already do everything with jl.Class. That > means, > for example, we had to invent the primitive mirrors a long time ago, so that > you could describe a field or method parameter of type int. "Fixing" this is > not really on the table right now. > - The language has given this some surface; you can say `int.class` (which > maps > to`getstatic Integer.TYPE`.) > Given all this, reflection needs to be able to differentiate between LFoo and > QFoo, which suggests a second mirror. And the user needs to be able to express > this mirror to call getMethod() or Lookup::findXxx (and to interpret the > results of reflective lookups). But maybe we don't need to give it so much air > *in the language*, especially given how confusing it is because the language > and VM views (which reflection is closer to) don't align entirely. > Rather than talking about the ref vs val mirror (which is confusing, because > getClass returns the same thing whether something is stored in a .ref or a > .val), perhaps we can reframe in terms of the "class mirror and the flattened > representation mirror", or the "primary mirror and the restricted mirror", or > some other such (though, even the term "mirror" is foreign to users, since all > the see is jl.Class.) > In this model, Point.class would be the only mirror expressible directly in > the > language, and it would always be the primary mirror. Reflection users would > need to navigate to the secondary mirror through something like > `Point.class.secondaryMirror()` or some other method on jl.Class. By not > putting this in the language, we avoid some of the confusing aspects of this > story. +1 RĂ©mi > On 6/23/2021 11:13 AM, Brian Goetz wrote: >> In working through the details of reflective support in JEP 401, I think >> we've >> fallen into a slight "false consistency" regarding class literals. (This is a >> complicated network with many interrelated moving parts, so I'm not looking >> for >> "quick answers" here, as much as balancing global consistency with an >> understandable user story.) >> In the language, a primitive class declaration gives rise to a class (P) and >> three types: P, P.ref and P.val. P is an alias for one of them; most of the >> time, it's an alias for P.val, but for migrated value-based class, it's an >> alias for P.ref. The language has a concept for tuning this mapping, >> currently >> under the name `ref-default`. (The VM would like to remain ignorant of >> details >> like this.) >> In the VM, there is one class (P), whose instances can have two >> representations, >> flattened and indirect, described by two descriptors (QP and LP). >> The VM and reflection need mirrors for both of these descriptor types. This >> is a >> problem we face today to some degree for the primitive types; there's a >> "neutered" mirror for int.class that doesn't correspond to an actual declared >> class with limited operational capability. These "secondary" mirrors are only >> used for reflection / method handles, to reflect method and field >> descriptors. >> We double down on this story for mirrors for primitive classes. Each >> primitive >> class has a primary (unrestricted) mirror corresponding to the L descriptor, >> and a secondary (restricted, flattened, possibly null-free) mirror >> corresponding to the Q descriptor. The secondary mirror has only one job: >> reflecting Q descriptors in method and field descriptors (and supporting >> method >> handles in their emulation of same.) >> When you ask an object for getClass(), it always hands back the primary >> mirror. >> As a bonus, this has a nice compatibility story with Integer today; >> reflective >> code is used to testing for Integer.class when an int is expected (reflection >> is always boxed), so this will continue to work, because instances of `int` >> will report `Integer.class` from `getClass()`. >> All of this feels like the low-energy-state for extending mirrors to >> primitives. >> Here's where I think we bobbled a little bit, and can correct. >> Currently, we say "Well, if Point is an alias for Point.val, then Point.class >> must be Point.val.class". This is consistent, but kind of useless. Because >> then, for example: >> void puzzler() { >> assertEquals(Point.class, new Point(0,0).getClass()); >> } >> will fail (unless Point is ref-default, at which point it succeeds.) I think >> the >> mistake we made is too literally following the model of "Point is an alias >> for >> Point.val". So, I'm proposing a slight adjustment, that treats the >> unqualified >> class name contextually: >> - 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.