Let me comment on Maurizio's comments, and add some more.

In 1.1, the spec for enum types has been used as a template, which is fine, but the summary of enum types has always been poor: the singular/plural construction is weird, and the mixing of kinds (class v. object) is horrible. The JLS is not the place to sling the term "enum" and sometimes have it mean the type and something the instance of the type; similarly, for "record". Notice that 8.9 never says "enum" on its own. Still, since people talk about "enums" and "records" a lot, the JLS should claim those terms -- as the types. For the avoidance of doubt, it's fine for this new draft spec to clarify/align the specification of older features. So:

Enums are a special kind of class that support the definition of small sets of values which can then be used in a type safe manner. Unlike enumerations in other languages, enums may have their own methods.

Records are a special kind of class that support the compact expression of simple objects that serve as aggregates of values.

The intro to ch.8 is the place to say more about enums and records. It is unfortunate that enums aren't covered there, e.g., the only way you learn that you can switch over an enum type is via an example buried in 8.9. For records, a description like "shallowly immutable" would be OK, since there's room to explain. The earlier the mention of components as a shorthand for fields, the easier and less dry will be 8.10 and especially 8.10.1.

On 9/3/2019 6:40 AM, Maurizio Cimadamore wrote:
* in 8.10.1:

"Each record component in the /RecordHeader/ declares one |private final| field in the record class whose name is same as the /Identifier/ in the record component."

This seems a bit early. Also, you repeat the same in 8.10.3, which is arguably a much better place?

Let's dial back the focus on the record header; compare with how 8.4 introduces `MethodHeader` but never utters "header" in narrative text.

-----
8.10.1  Record Components

[This paragraph has two normative sentences in it.] The _record components_ of a record type, if any, are specified in the header of the record declaration. [The X of the type being specified in the Y of the declaration comports with how 8.10 says that the declaration specifies the type.] [The grammar allows a component-less record type; I'm following that. See 8.4.1 for another "if any" usage.] Each record component corresponds to an implicitly declared field of the record type (8.10.3).

RecordHeader:    [Following style of 14.2 and 8.4/8.4.1]
    `(` _[_RecordComponents_]_ `)`

RecordComponents:
    RecordComponent _{_ `,` RecordComponent _}_

// Grammar should always be followed by basic well-formedness rules.

It is a compile-time error for a record header to specify two record components with the same name.

It is a compile-time error for a record header to specify a record component with the name clone, finalize, getClass, hashCode, notify, notifyAll, readObjectNoData, readResolve, serialPersistentFields, toString, wait, or writeReplace. [It's right to say this here, where we're talking about components themselves -- we're hinting to compilers that the error should be about component with a bad name, not field-you-can't-see-in-source having a bad name.]
-----

8.10.2 says "It is a compile-time error for the body of a record declaration to contain non-static field declarations. All non-static fields should be declared as record components in the record header."

Use "should" very very sparingly in normative text. This is the place for a note about how, "in the judgment of the designers of the Java programming language", the only per-instance state of a record type should be the components -- and yes, that state is final, so plainly a record type isn't syntactic sugar, it expresses something about the aggregate. Also a good place to compare enum types -- instance controlled, but state can be arbitrary -- versus record types -- not instance controlled, but state is locked down. (Comparison not because a developer is wondering whether to use an enum type or a record type; comparison because, well, this is the JLS -- it introduced two "special" kinds of class type, so needs to teach their outlines before anyone asks.)

8.10.4: Consider how 8.8.9 states that "If a class contains no constructor declarations, then a default constructor is implicitly declared." and how 8.9.2 modifies that with "In an enum declaration with no constructor declarations, a default constructor is implicitly declared. The default constructor is private, has no formal parameters, and has no throws clause." ... 8.10.4 should follow suit. At the same time, there is value in the term "canonical ctor" -- in a normal class, if you explicitly declare an arbitrary ctor, then you don't get a default ctor implicitly declared, whereas in a record type, if you explicitly declare an arbitrary ctor, then you still get a canonical ctor implicitly declared. I recognize this is a more imperative description than the declarative sentences which currently start 8.10.4, but I really couldn't get the whole picture until I wrote out the above.

I also want to bring in the class instance creation expression that invokes a ctor (yes, that's the phrasing from 8.8) with a bunch of arguments. Given record R(int x,int y), you can create it via `new R("Hi Bob")` if you wrote a suitable ctor, but by policy we're also going to give you the option of `new R(1,2)` because the language has a first-class idea of the important state of the record. That needs to be memorialized in the JLS.

Separately, there is a compact ctor declaration form whose body is magic. That comes at the end, after everything has been said about canonical ctor. Currently the compact form is grabbing too much focus.

Here's a plan:

-----
To support proper initialization of its record components, a record type does not implicitly declare a default constructor (8.8.9). Instead, a record type has a _canonical constructor_ that is declared either explicitly or implicitly, and whose formal parameters correspond to the record components. [This para is meant to be suggestive, not precise.]

For a record type R with components C_1, ..., C_n, the canonical constructor has the form:
[bullet list of precise statements about access, params, no-throws]

If the canonical constructor is explicitly declared, then ... [Umm, what are the rules? Can I call super(..) anywhere? Can it throw? What? I'm not talking about the compact form, forget that, it's a distraction.]

It is a compile-time error if a record declaration contains more than one explicit declaration of the canonical constructor.

If the canonical constructor is not explicitly declared, then it is implicitly declared. Its body ...

If a record declaration contains constructor declarations other than the canonical constructor, then they follow the same rules as for a normal class declaration. ["may be of the same form" is vague; a fixed form is a feature of default ctors only; all other ctors of a class just have a bunch of rules.]

8.10.5  Compact Record Constructors

A _compact constructor declaration_ is a form of constructor declaration that explicitly declares the canonical constructor.

CompactConstructorDeclaration: [Present this here, not 8.10.2, which should forward-ref this section.] {Annotation} {ConstructorModifier} [TypeParameters] SimpleTypeName [Throws] ConstructorBody

...
-----

* initialised - UK vs US english - not sure which we should use, but I see other uses of 'initialized' around

US English.

* 9.7.4:

we say this:

"
It is a compile-time error if an annotation of type /T/ is syntactically a modifier for:

[...]

a record component but T is not applicable to record component declarations, type contexts, type parameter declarations, field declarations, or method declarations."

But it's not super clear what happens to the annotations that are actually correct and how they are propagated. Should e.g. the fact that a field decl anno on a record element is propagated on its synthetic private field be covered by the JLS?

The private fields aren't synthetic (generated by a compiler as a private implementation detail), they're mandated ("implicitly declared"). 8.10.3 says that the implicitly declared fields have the annos from the components, great, done.

Alex

Reply via email to