<!DOCTYPE html><html><head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>
<font size="4" face="monospace">Here's a snapshot of where my head
is at with respect to extending the record goodies (including
pattern matching) to a broader range of classes, deconstructors
for classes and interfaces, and compatible evolution of records.
Hopefully this will unblock quite a few things.<br>
<br>
As usual, let's discuss concepts and directions rather than
syntax. <br>
<br>
<br>
<br>
# Data-oriented Programming for Java: Beyond records<br>
<br>
Everyone loves records; they allow us to create shallowly
immutable data holder<br>
classes -- which we can think of as "nominal tuples" -- derived
from a concise<br>
state description, and to destructure records through pattern
matching. But<br>
records have strict constraints, and not all data holder classes
fit into the<br>
restrictions of records. Maybe they have some mutable state, or
derived or<br>
cached state that is not part of the state description, or their
representation<br>
and their API do not match up exactly, or they need to break up
their state<br>
across a hierarchy. In these classes, even though they may also
be “data<br>
holders”, the user experience is like falling off a cliff. Even a
small<br>
deviation from the record ideal means one has to go back to a
blank slate and<br>
write explicit constructor declarations, accessor method
declarations, and<br>
Object method implementations -- and give up on destructuring
through pattern<br>
matching. <br>
<br>
Since the start of the design process for records, we’ve kept in
mind the goal<br>
of enabling a broader range of classes to gain access to the
"record goodies":<br>
reduced declaration burden, participating in destructuring, and
soon,<br>
[reconstruction](<a class="moz-txt-link-freetext" href="https://openjdk.org/jeps/468">https://openjdk.org/jeps/468</a>). During the design
of records, we<br>
also explored a number of weaker semantic models that would allow
for greater<br>
flexibility. While at the time they all failed to live up to the
goals _for<br>
records_, there is a weaker set of semantic constraints we can
impose that<br>
allows for more flexibility and still enables the features we
want, along with<br>
some degree of syntactic concision that is commensurate with the
distance from<br>
the record-ideal, without fall-off-the-cliff behaviors. <br>
<br>
Records, sealed classes, and destructuring with record patterns
constitute the<br>
first feature arc of "data-oriented programming" for Java. After
considering<br>
numerous design ideas, we're now ready to move forward with the
next "data<br>
oriented programming" feature arc: _carrier classes_ (and
interfaces.)<br>
<br>
## Beyond record patterns<br>
<br>
Record patterns allow a record instance to be destructured into
its components.<br>
Record patterns can be used in `instanceof` and `switch`, and when
a record<br>
pattern is also exhaustive, will be usable in the upcoming
[_pattern assignment<br>
statement_](<a class="moz-txt-link-freetext" href="https://mail.openjdk.org/pipermail/amber-spec-experts/2026-January/004306.html">https://mail.openjdk.org/pipermail/amber-spec-experts/2026-January/004306.html</a>)
feature. <br>
<br>
In exploring the question "how will classes be able to participate
in the same<br>
sort of destructuring as records", we had initially focused on a
new form of<br>
declaration in a class -- a "deconstructor" -- that operated as a
constructor in<br>
reverse. Just as a constructor takes component values and produces
an aggregate<br>
instance, a deconstructor would take an aggregate instance and
recover its<br>
component values. <br>
<br>
But as this exploration played out, the more interesting question
turned out to<br>
be: which classes are suitable for destructuring in the first
place? And the<br>
answer to that question led us to a different approach for
expressing<br>
deconstruction. The classes that are suitable for destructuring
are those that,<br>
like records, are little more than carriers for a specific tuple
of data. This<br>
is not just a thing that a class _has_, like a constructor or
method, but<br>
something a class _is_. And as such, it makes more sense to
describe<br>
deconstruction as a top-level property of a class. This, in turn,
leads to a<br>
number of simplifications. <br>
<br>
## The power of the state description<br>
<br>
Records are a semantic feature; they are only incidentally
concise. But they<br>
_are_ concise; when we declare a record<br>
<br>
record Point(int x, int y) { ... }<br>
<br>
we automatically get a sensible API (canonical constructor,
deconstruction<br>
pattern, accessor methods for each component) and implementation
(fields,<br>
constructor, accessor methods, Object methods.) We can explicitly
specify most<br>
of these (except the fields) if we like, but most of the time we
don't have to,<br>
because the default is exactly what we want.<br>
<br>
A record is a shallowly-immutable, final class whose API and
representation are<br>
_completely defined_ by its _state description_. (The slogan for
records is<br>
"the state, the whole state, and nothing but the state.") The
state description<br>
is the ordered list of _record components_ declared in the
record's header. A<br>
component is more than a mere field or accessor method; it is an
API element on<br>
its own, describing a state element that instances of the class
have. <br>
<br>
The state description of a record has several desirable
properties: <br>
<br>
- The components in the order specified, are the _canonical_
description of the<br>
record's state.<br>
- The components are the _complete_ description of the record’s
state. <br>
- The components are _nominal_; their names are a committed part
of the<br>
record's API. <br>
<br>
Records derive their benefits from making two commitments:<br>
<br>
- The _external_ commitment that the data-access API of a record
(constructor,<br>
deconstruction pattern, and component accessor methods) is
defined by the<br>
state description. <br>
- The _internal_ commitments that the _representation_ of the
record (its<br>
fields) is also completely defined by the state description.<br>
<br>
These semantic properties are what enable us to derive almost
everything about<br>
records. We can derive the API of the canonical constructor
because the state<br>
description is canonical. We can derive the API for the component
accessor<br>
methods because the state description is nominal. And we can
derive a<br>
deconstruction pattern from the accessor methods because the state
description<br>
is complete (along with sensible implementations for the
state-related `Object`<br>
methods.)<br>
<br>
The internal commitment that the state description is also the
representation<br>
allows us to completely derive the rest of the implementation.
Records get a<br>
(private, final) field for each component, but more importantly,
there is a<br>
clear mapping between these fields and their corresponding
components, which is<br>
what allows us to derive the canonical constructor and accessor
method<br>
implementations. <br>
<br>
Records can additionally declare a _compact constructor_ that
allows us to elide<br>
the boilerplate aspects of record constructors -- the argument
list and field<br>
assignments -- and just specify the code that is _not_
mechanically derivable.<br>
This is more concise, less error-prone, and easier to read:<br>
<br>
record Rational(int num, int denom) { <br>
Rational { <br>
if (denom == 0)<br>
throw new IllegalArgumentException("denominator
cannot be zero");<br>
}<br>
}<br>
<br>
is shorthand for the more explicit<br>
<br>
record Rational(int num, int denom) { <br>
Rational(int num, int denom) { <br>
if (denom == 0)<br>
throw new IllegalArgumentException("denominator
cannot be zero");<br>
this.num = num;<br>
this.denom = denom;<br>
}<br>
}<br>
<br>
While compact constructors are pleasantly concise, the more
important benefit is<br>
that by eliminating the mechanically derivable code, the "more
interesting" code<br>
comes to the fore. <br>
<br>
Looking ahead, the state description is a gift that keeps on
giving. These<br>
semantic commitments are enablers for a number of potential future
language and<br>
library features for managing object lifecycle, such as: <br>
<br>
- [Reconstruction](<a class="moz-txt-link-freetext" href="https://openjdk.org/jeps/468">https://openjdk.org/jeps/468</a>) of record
instances, allowing<br>
the appearance of controlled mutation of record state.<br>
- Automatic marshalling and unmarshalling of record instances.<br>
- Instantiating or destructuring record instances identifying
components<br>
nominally rather than positionally.<br>
<br>
### Reconstruction<br>
<br>
JEP 468 proposes a mechanism by which a new record instance can be
derived from<br>
an existing one using syntax that is evocative of direct mutation,
via a `with`<br>
expression: <br>
<br>
record Complex(double re, double im) { }<br>
Complex c = ...<br>
Complex cConjugate = c with { im = -im; }; <br>
<br>
The block on the right side of `with` can contain any Java
statements, not just<br>
assignments. It is enhanced with mutable variables (_component
variables_) for<br>
each component of the record, initialized to the value of that
component in the<br>
record instance on the left, the block is executed, and a new
record instance is<br>
created whose component values are the ending values of the
component variables. <br>
<br>
A reconstruction expression implicitly destructures the record
instance using<br>
the canonical deconstruction pattern, executes the block in a
scope enhanced<br>
with the component variables, and then creates a new record using
the canonical<br>
constructor. Invariant checking is centralized in the canonical
constructor, so<br>
if the new state is not valid, the reconstruction will fail. JEP
468 has been<br>
"on hold" for a while, primarily because we were waiting for
sufficient<br>
confidence that there was a path to extending it to suitable
classes before<br>
committing to it for records. The ideal path would be for those
classes to also<br>
support a notion of canonical constructor and deconstruction
pattern. <br>
<br>
Careful readers will note a similarity between the transformation
block of a<br>
`with` expression and the body of a compact constructor. In both
cases, the<br>
block is "preloaded" with a set of component variables,
initialized to suitable<br>
starting values, the block can mutate those variables as desired,
and upon<br>
normal completion of the block, those variables are passed to a
canonical<br>
constructor to produce the final result. The main difference is
where the<br>
starting values come from; for a compact constructor, it is from
the constructor<br>
parameters, and for a reconstruction expression, it is from the
canonical<br>
deconstruction pattern of the source record to the left of `with`.<br>
<br>
### Breaking down the cliff<br>
<br>
Records make a strong semantic commitment to derive both their API
and<br>
representation from the state description, and in return get a lot
of help from<br>
the language. We can now turn our attention to smoothing out "the
cliff" --<br>
identifying weaker semantic commitments that classes can make that
would still<br>
allow classes to get _some_ help from the language. And ideally,
the amount of<br>
help you give up would be proportional to the degree of deviation
from the<br>
record ideal.<br>
<br>
With records, we got a lot of mileage out of having a complete,
canonical,<br>
nominal state description. Where the record contract is sometimes
too<br>
constraining is the _implementation_ contract that the
representation aligns<br>
exactly with the state description, that the class is final, that
the fields are<br>
final, and that the class may not extend anything but `Record`. <br>
<br>
Our path here takes one step back and one step forward: keeping
the external<br>
commitment to the state description, but dropping the internal
commitment that<br>
the state description _is_ the representation -- and then _adding
back_ a simple<br>
mechanism for mapping fields representing components back to their
corresponding<br>
components, where practical. (With records, because we derive the<br>
representation from the state description, this mapping can be
safely inferred.) <br>
<br>
As a thought experiment, imagine a class that makes the external
commitment to a<br>
state description -- that the state description is a complete,
canonical,<br>
nominal description of its state -- but is on its own to provide
its<br>
representation. What can we do for such a class? Quite a bit,
actually. For<br>
all the same reasons we can for records, we can derive the API
requirement for a<br>
canonical constructor and component accessor methods. From there,
we can derive<br>
both the requirement for a canonical deconstruction pattern, and
also the<br>
implementation of the deconstruction pattern (as it is implemented
in terms of<br>
the accessor methods). And since the state description is
complete, we can<br>
further derive sensible default implementations of the Object
methods `equals`,<br>
`hashCode`, and `toString` in terms of the accessor methods as
well. And given<br>
that there is a canonical constructor and deconstruction pattern,
it can also<br>
participate in reconstruction. The author would just have to
provide the<br>
fields, accessor methods, and canonical constructor. This is good
progress, but<br>
we'd like to do better.<br>
<br>
What enables us to derive the rest of the implementation for
records (fields,<br>
constructor, accessor methods, and Object methods) is the
knowledge of how the<br>
representation maps to the state description. Records commit to
their state<br>
description _being_ the representation, so is is a short leap from
there to a<br>
complete implementation. <br>
<br>
To make this more concrete, let's look at a typical "almost
record" class, a<br>
carrier for the state description `(int x, int y,
Optional<String> s)` but which<br>
has made the representation choice to internally store `s` as a
nullable<br>
`String`. <br>
<br>
```<br>
class AlmostRecord { <br>
private final int x;<br>
private final int y;<br>
private final String s; // *<br>
<br>
public AlmostRecord(int x, int y, Optional<String> s) { <br>
this.x = x;<br>
this.y = y;<br>
this.s = s.orElse(null); // *<br>
}<br>
<br>
public int x() { return x; }<br>
public int y() { return y; }<br>
public Optional<String> s() { <br>
return Optional.ofNullable(s); // *<br>
}<br>
<br>
public boolean equals(Object other) { ... } // derived
from x(), y(), s()<br>
public int hashCode() { ... } // "<br>
public String toString() { ... } // "<br>
}<br>
```<br>
<br>
The main differences between this class and the expansion of its
record analogue<br>
are the lines marked with a `*`; these are the ones that deal with
the disparity<br>
between the state description and the actual representation. It
would be nice<br>
if the author of this class _only_ had to write the code that was
different from<br>
what we could derive for a record; not only would this be
pleasantly concise,<br>
but it would mean that all the code that _is_ there exists to
capture the<br>
differences between its representation and its API. <br>
<br>
## Carrier classes<br>
<br>
A _carrier class_ is a normal class declared with a state
description. As with<br>
a record, the state description is a complete, canonical, nominal
description of<br>
the class's state. In return, the language derives the same API
constraints as<br>
it does for records: canonical constructor, canonical
deconstruction pattern,<br>
and component accessor methods.<br>
<br>
class Point(int x, int y) { // class, not
record!<br>
// explicitly declared representation<br>
<br>
...<br>
<br>
// must have a constructor taking (int x, int y)<br>
// must have accessors for x and y<br>
// supports a deconstruction pattern yielding (int x, int
y)<br>
}<br>
<br>
Unlike a record, the language makes no assumptions about the
object's<br>
representation; the class author has to declare that just as with
any other<br>
class. <br>
<br>
Saying the state description is "complete" means that it carries
all the<br>
“important” state of the class -- if we were to extract this state
and recreate<br>
the object, that should yield an “equivalent” instance. As with
records, this<br>
can be captured by tying together the behavior of construction,
accessors, and<br>
equality:<br>
<br>
```<br>
Point p = ...<br>
Point q = new Point(p.x(), p.y());<br>
assert p.equals(q);<br>
```<br>
<br>
We can also derive _some_ implementation from the information we
have so far; we<br>
can derive sensible implementations of the `Object` methods
(implemented in terms<br>
of component accessor methods) and we can derive the canonical
deconstruction<br>
pattern (again in terms of the component accessor methods). And
from there, we<br>
can derive support for reconstruction (`with` expressions.)
Unfortunately, we<br>
cannot (yet) derive the bulk of the state-related implementation:
the canonical<br>
constructor and component accessor methods. <br>
<br>
### Component fields and accessor methods<br>
<br>
One of the most tedious aspects of data-holder classes is the
accessor methods;<br>
there are often many of them, and they are almost always pure
boilerplate. Even<br>
though IDEs can reduce the writing burden by generating these for
us, readers<br>
still have to slog through a lot of low-information code -- just
to learn that<br>
they didn't actually need to slog through that code after all. We
can derive<br>
the implementation of accessor methods for records because records
make the<br>
internal commitment that the components are all backed with
individual fields<br>
whose name and type align with the state description. <br>
<br>
For a carrier class, we don't know whether _any_ of the components
are directly<br>
backed by a single field that aligns to the name or type of the
component. But<br>
it is a pretty good bet that many carrier class components will do
exactly this<br>
for at least _some_ of their fields. If we can tell the language
that this<br>
correspondence is not merely accidental, the language can do more
for us.<br>
<br>
We do so by allowing suitable fields of a carrier class to be
declared as<br>
`component` fields. (As usual at this stage, syntax is
provisional, but not <br>
currently a topic for discussion.) A component field must have
the same name<br>
and type as a component of the current class (though it need not
be `private` or<br>
`final`, as record fields are.) This signals that this field _is_
the<br>
representation for the corresponding component, and hence we can
derive the<br>
accessor method for this component as well. <br>
<br>
```<br>
class Point(int x, int y) { <br>
private /* mutable */ component int x;<br>
private /* mutable */ component int y;<br>
<br>
// must have a canonical constructor, but (so far) must be
explicit<br>
public Point(int x, int y) { <br>
this.x = x;<br>
this.y = y;<br>
}<br>
<br>
// derived implementations of accessors for x and y<br>
// derived implementations of equals, hashCode, toString<br>
}<br>
```<br>
<br>
This is getting better; the class author had to bring the
representation and the<br>
mapping from representation to components (in the form of the
`component`<br>
modifier), and the canonical constructor. <br>
<br>
### Compact constructors<br>
<br>
Just as we are able to derive the accessor method implementation
if we are<br>
given an explicit correspondence between a field and a component,
we can do the<br>
same for constructors. For this, we build on the notion of
_compact<br>
constructors_ that was introduced for records. <br>
<br>
As with a record, a compact constructor in a carrier class is a
shorthand for a<br>
canonical constructor, which has the same shape as the state
description, but<br>
which is freed of the responsibility of actually committing the
ending value of<br>
the component parameters to the fields. The main difference is
that for a<br>
record, _all_ of the components are backed by a component field,
whereas for a<br>
carrier class, only some of them might be. But we can generalize
compact<br>
constructors by freeing the author of the responsibility to
initialize the<br>
_component_ fields, while leaving them responsible for
initializing the rest of<br>
the fields. In the limiting case where all components are backed
by component<br>
fields, and there is no other logic desired in the constructor,
the compact<br>
constructor may be elided. <br>
<br>
For our mutable `Point` class, this means we can elide nearly
everything, except<br>
the field declarations themselves: <br>
<br>
```<br>
class Point(int x, int y) { <br>
private /* mutable */ component int x;<br>
private /* mutable */ component int y;<br>
<br>
// derived compact constructor<br>
// derived accessors for x, y<br>
// derived implementations of equals, hashCode, toString<br>
}<br>
```<br>
<br>
We can think of this class as having an implicit empty compact
constructor,<br>
which in turn means that the component fields `x` and `y` are
initialized from<br>
their corresponding constructor parameters. There are also
implicitly derived<br>
accessor methods for each component, and implementations of
`Object` methods<br>
based on the state description. <br>
<br>
This is great for a class where all the components are backed by
fields, but<br>
what about our `AlmostRecord` class? The story here is good as
well; we can<br>
derive the accessor methods for the components backed by component
fields, and<br>
we can elide the initialization of the component fields from the
compact<br>
constructor, meaning that we _only_ have to specify the code for
the parts that<br>
deviate from the "record ideal": <br>
<br>
```<br>
class AlmostRecord(int x, <br>
int y, <br>
Optional<String> s) { <br>
<br>
private final component int x;<br>
private final component int y;<br>
private final String s;<br>
<br>
public AlmostRecord { <br>
this.s = s.orElse(null); <br>
// x and y fields implicitly initialized<br>
}<br>
<br>
public Optional<String> s() { <br>
return Optional.ofNullable(s); <br>
}<br>
<br>
// derived implementation of x and y accessors<br>
// derived implementation of equals, hashCode, toString<br>
}<br>
```<br>
<br>
Because so many real-world almost-records differ from their record
ideal in<br>
minor ways, we expect to get a significant concision benefit for
most carrier<br>
classes, as we did for `AlmostRecord`. As with records, if we
want to<br>
explicitly implement the constructor, accessor methods, or
`Object` methods, we<br>
are still free to do so. <br>
<br>
### Derived state<br>
<br>
One of the most frequent complaints about records is the inability
to derive<br>
state from the components and cache it for fast retrieval. With
carrier<br>
classes, this is simple: declare a non-component field for the
derived quantity,<br>
initialize it in the constructor, and provide an accessor: <br>
<br>
```<br>
class Point(int x, int y) { <br>
private final component int x;<br>
private final component int y;<br>
private final double norm;<br>
<br>
Point { <br>
norm = Math.hypot(x, y);<br>
}<br>
<br>
public double norm() { return norm; }<br>
<br>
// derived implementation of x and y accessors<br>
// derived implementation of equals, hashCode, toString<br>
}<br>
```<br>
<br>
### Deconstruction and reconstruction<br>
<br>
Like records, carrier classes automatically acquire deconstruction
patterns that<br>
match the canonical constructor, so we can destructure our `Point`
class as if<br>
it were a record: <br>
<br>
case Point(var x, var y): <br>
<br>
Because reconstruction (`with`) derives from a canonical
constructor and<br>
corresponding deconstruction pattern, when we support
reconstruction of records,<br>
we will also be able to do so for carrier classes:<br>
<br>
point = point with { x = 3; }<br>
<br>
## Carrier interfaces<br>
<br>
A state description makes sense on interfaces as well. It makes
the statement<br>
that the state description is a complete, canonical, nominal
description of the<br>
interface's state (subclasses are allowed to add additional
state), and<br>
accordingly, implementations must provide accessor methods for the
components.<br>
This enables such interfaces to participate in pattern matching:<br>
<br>
```<br>
interface Pair<T,U>(T first, U second) {<br>
// implicit abstract accessors for first() and second()<br>
}<br>
<br>
...<br>
<br>
if (o instanceof Pair(var a, var b)) { ... }<br>
```<br>
<br>
Along with the upcoming feature for pattern assignment in
foreach-loop headers,<br>
if `Map.Entry` became a carrier interface (which it will), we
would be able to<br>
iterate a `Map` like:<br>
<br>
for (Map.Entry(var key, var val) : map.entrySet()) { ... }<br>
<br>
It is a common pattern in libraries to export an interface that is
sealed to a<br>
single private implementation. In this pattern, the interface and<br>
implementation can share a common state description: <br>
<br>
```<br>
public sealed interface Pair<T,U>(T first, U second) { }<br>
<br>
private record PairImpl<T, U>(T first, U second) implements
Pair<T, U> { }<br>
```<br>
<br>
Compared to the old way of doing this, we get enhanced semantics,
better type<br>
checking, and more concision. <br>
<br>
### Extension<br>
<br>
The main obligation of a carrier class author is to ensure that
the fundamental<br>
claim -- that the state description is a complete, canonical,
nominal<br>
description of the object's state -- is actually true. This does
not rule out<br>
having the representation of a carrier class spread out over a
hierarchy, so<br>
unlike records, carrier classes are not required to be final or
concrete, nor<br>
are they restricted in their extension.<br>
<br>
There are several cases that arise when carrier classes can
participate in<br>
extension:<br>
<br>
- A carrier class extends a non-carrier class;<br>
- A non-carrier class extends a carrier class;<br>
- A carrier class extends another carrier class, where all of the
superclass<br>
components are subsumed by the subclass state description; <br>
- A carrier class extends another carrier class, but there are
one or more<br>
superclass components that are not subsumed by the subclass
state<br>
description.<br>
<br>
Extending a non-carrier class with a carrier class will usually be
motiviated by<br>
the desire to "wrap" a state description around an existing
hierarchy which we<br>
cannot or do not want to modify directly, but we wish to gain the
benefits of<br>
deconstruction and reconstruction. Such an implementation would
have to ensure<br>
that the class actually conforms to the state description, and
that the<br>
canonical constructor and component accessors are implemented. <br>
<br>
When one carrier class extends another, the more straightforward
case is that it<br>
simply adds new components to the state description of the
superclass. For<br>
example, given our `Point` class:<br>
<br>
```<br>
class Point(int x, int y) { <br>
component int x; <br>
component int y;<br>
<br>
// everything else for free!<br>
}<br>
```<br>
<br>
we can use this as the base class for a 3d point class: <br>
<br>
```<br>
class Point3d(int x, int y, int z) extends Point { <br>
component int z;<br>
<br>
Point3d {<br>
super(x, y);<br>
}<br>
}<br>
```<br>
<br>
In this case -- because the superclass components are all part of
the subclass<br>
state description -- we can actually omit the constructor as well,
because we<br>
can derive the association between subclass components and
superclass<br>
components, and thereby derive the needed super-constructor
invocation. So we<br>
could actually write:<br>
<br>
```<br>
class Point3d(int x, int y, int z) extends Point { <br>
component int z;<br>
<br>
// everything else for free!<br>
}<br>
```<br>
<br>
One might think that we would need some marking on the `x` and `y`
components of<br>
`Point3d` to indicate that they map to the corresponding
components of `Point`,<br>
as we did for associating component fields with their
corresponding components.<br>
But in this case, we need no such marking, because there is no way
that an `int<br>
x` component of `Point` and an `int x` component of its subclass
could possibly<br>
refer to different things -- since they both are tied to the same
`int x()`<br>
accessor methods. So we can safely infer which subclass
components are managed<br>
by superclasses, just by matching up their names and types.<br>
<br>
In the other carrier-to-carrier extension case, where one or more
superclass<br>
components are _not_ subsumed by the subclass state description,
it is necessary<br>
to provide an explicit `super` constructor call in the subclass
constructor. <br>
<br>
A carrier class may be also declared abstract; the main effect of
this is that<br>
we will not derive `Object` method implementations, instead
leaving that for the<br>
subclass to do.<br>
<br>
### Abstract records<br>
<br>
This framework also gives us an opportunity to relax one of the
restrictions on<br>
records: that records can't extend anything other than
`java.lang.Record`. We<br>
can also allow records to be declared `abstract`, and for records
to extend<br>
abstract records. <br>
<br>
Just as with carrier classes that extend other carrier classes,
there are two<br>
cases: when the component list of the superclass is entirely
contained within<br>
that of the subclass, and when one or more superclass components
are derived<br>
from subclass components (or are constant), but are not components
of the<br>
subclass itself. And just as with carrier classes, the main
difference is<br>
whether an explicit `super` call is required in the subclass
constructor. <br>
<br>
When a record extends an abstract record, any components of the
subclass that<br>
are also components of the superclass do not implicitly get
component fields in<br>
the subclass (because they are already in the superclass), and
they inherit the<br>
accessor methods from the superclass. <br>
<br>
### Records are carriers too<br>
<br>
With this framework in place, records can now be seen to be "just"
carrier<br>
classes that are implicitly final, extend `java.lang.Record`, that
implicitly<br>
have private final component fields for each component, and can
have no other<br>
fields. <br>
<br>
## Migration compatibility <br>
<br>
There will surely be some existing classes that would like to
become carrier<br>
classes. This is a compatible migration as long as none of the
mandated members<br>
conflict with existing members of the class, and the class adheres
to the<br>
requirement that the state description is a complete, canonical,
and nominal<br>
description of the object state. <br>
<br>
### Compatible evolution of records and carrier classes<br>
<br>
To date, libraries have been reluctant to use records in public
APIs because<br>
of the difficulty of evolving them compatibly. For a record: <br>
<br>
```<br>
record R(A a, B b) { }<br>
```<br>
<br>
that wants to evolve by adding new components:<br>
<br>
```<br>
record R(A a, B b, C c, D d) { }<br>
```<br>
<br>
we have several compatibility challenges to manage. As long as we
are only<br>
adding and not removing/renaming, accessor method invocations will
continue to<br>
work. And existing constructor invocations can be allowed to
continue work by<br>
explicitly adding back a constructor that has the old shape: <br>
<br>
```<br>
record R(A a, B b, C c, D d) { <br>
<br>
// Explicit constructor for old shape required<br>
public R(A a, B b) { <br>
this(a, b, DEFAULT_C, DEFAULT_D);<br>
}<br>
<br>
}<br>
```<br>
<br>
But, what can we do about existing uses of record _patterns_?
While the<br>
translation of record patterns would make adding components
binary-compatible,<br>
it would not be source-compatible, and there is no way to
explicitly add a<br>
deconstruction pattern for the old shape as we did with the
constructor. <br>
<br>
We can take advantage of the simplification offered by there being
_only_ the<br>
canonical deconstruction pattern, and allow uses of deconstruction
patterns to<br>
supply nested patterns for any _prefix_ of the component list. So
for the<br>
evolved record R: <br>
<br>
case R(P1, P2)<br>
<br>
would be interpreted as:<br>
<br>
case R(P1, P2, _, _)<br>
<br>
where `_` is the match-all pattern. This means that one can
compatibly evolve a<br>
record by only adding new components at the end, and adding a
suitable<br>
constructor for compatibility with existing constructor
invocations.<br>
<br>
</font>
</body>
</html>