FYI, proposed javax.lang.model changes for records
Joe Darcy
joe.darcy at oracle.com
Tue Oct 8 00:52:34 UTC 2019
Hello,
On 10/7/2019 3:51 PM, Maurizio Cimadamore wrote:
> Hi Vicente,
> looking at the ElementKind construct, it is very nice to see that we
> have now a nice 1-1 mapping between:
>
> - Record/Record_Component
> - Enum/Enum_Constant
>
> That said, from an API perspective, while both enums and records are
> TypeElement (so we're consistent there), there's nothing in the API to
> model enum constants directly, while now we do have a way to model
> record components.
>
> I think this asymmetry should be rectified, eventually, but I do not
> think this is a fault of the record API - I believe in reality what
> I'd wish is for enums to be more exposed in the element API, rather
> than being presented as a special kind of field (which is, I think,
> what happens now). Moreover, there's no way to ask a TypeElement
> representing an 'enum' questions like: give me all your constants.
Giving some context on the API design here, back in the original apt
modeling API, there was a more specific correspondence between the
interface hierarchy and the language concepts. For example, there were
interfaces for all of
* TypeDeclaration
* ClassDeclaration extends TypeDeclaration
* EnumDeclaration extends ClassDeclaration (adding getEnumConstants method)
* InterfaceDeclaration extends TypeDeclaration
* AnnotationTypeDeclaration extends InterfaceDeclaration
Based on experience with the apt API, wanting to make the replacement
API easier to evolve and easier to be implemented by multiple compilers,
we made the javax.lang.model API less specific. For example, all five
interfaces above map to javax.lang.model.TypeElement. The flip side is
if methods are conceptually hoisted up the interface hierarchy, because
several layers of interfaces are combined, more special cases in methods
will need to be defined in terms of vacuous results. For example, if
javax.lang.model.TypeElement::getSuperClass is called on an interface
type, a sentinel "no type" value is returned. Another consequence is
that methods analogous to "getEnumConstants" aren't necessarily were
you'd look for them. To keep the main modeling interfaces streamlined,
secondary functionality was moved over to the
javax.lang.model.util.Elements helper interface. There isn't a single
method in the current javax.lang.model API for returning the enum
constants; filtering on the enclosed objects whose element kind is
ENUM_CONSTANT is how I'd recommend coding the functionality in user code
and how javac's printing processor isolates the constants. It would also
be possible to add a method to return the constants to
javax.lang.model.util.Elements.
(If default methods were available when JSR 269 was being designed, it
is likely we would have made different API trade-offs and included more
convenience methods in the main interfaces.)
Vicente and I had various off-list discussions about how record
components should be represented in javax.lang.model. [1] One possible
model is what is proposed here, record components as a new direct
subinterface of Element.
I don't think it is absolutely necessary to give record components their
own modeling interface, but I think it is reasonable to do so given
their additional "conceptual size" compared to enum constants. For
example, a record component gets mapped into potentially a constructor
parameter, a private field, and an accessor method and can host
annotations that get copied down accordingly. That is "bigger" than the
enum constant mapping of to a static final field. [2] I wouldn't object
to a convenience method for getting the enum constants of a type , but I
think retrofitting a EnumConstantElement interface would be problematic
with respect to behavioral compatibility of existing visitors.
One of the main technical differences between javax.lang.model and the
older api apt API besides the leaner modeling approach is
javax.lang.model tackles the "expression problem" for coping with
evolving the language while the apt API was only intended for the Java
SE 5.0 iteration of the language. [3]
HTH,
-Joe
[1] The modeling choices include:
1) New top-level RecordComponentElement as a direct subinterface of
javax.lang.model.Element with matching kind.
2) New sub-interface of VariableElement; record components distinguished
by a new kind.
3) No new interface for record components, reuse VariableElements with a
RECORD_COMPONENT kind.
It is clearer how to make solutions 1) and 3) work for the visitors,
with 1) being the most straightforward if largest footprint on the API.
[2] A limitation of the current modeling of enum constants came to my
attention in the context of some recent work on an annotation processor
to check the declarations of serializable types. If an enum constant is
declared using the specialized enum constant syntax which implicitly
defines an anonymous class, there is no way through the existing API to
access the members of that class. To address this, the API could allow
enum constants to have a non-empty list of enclosed elements. However,
since this is, to my knowledge, the first time this limitation has come
up in about 15 years of using the API, I don't think it is a very urgent
problem.
[3] See https://en.wikipedia.org/wiki/Expression_problem for history and
discussion. The visitUnknown visitor methods and UnknownFooExceptions
are the mechanisms to allow well-written annotation processors to be run
More information about the compiler-dev
mailing list