Records feedback
Stephen Colebourne
scolebourne at joda.org
Fri Nov 22 00:40:07 UTC 2019
Overall, my view is that records are a reasonable stake in the ground
for the problem space. They will clearly have their uses, but those
uses will not be many of the cases where beans are currently used.
I suspect the main use case will be for messages at the boundaries of
a system, where developers are currently expected to write a mutable
POJO. However with no builder and no named parameters, these will be
horrible to construct from normal code once the record has more than a
few properties. I find this pretty concerning, but a future preview
could still fix the issue.
In our code, records would be insufficient to replace the vast
majority of beans, which are currently generated using Joda-Beans. Our
beans provide control over access, allow to omit fields from toString
(eg. password fields), have values cached from other fields, have
declarative validation, have a specific design pattern for optional,
have convenient builders (eg. overloads accepting a list and vargs).
Our beans meet the essential test of being immutable and being
entirely about state, yet they can't meet the very tightly drawn
limitations of records. We would not move to records unless 95% of
beans could be records, and maybe 100% as we would not want the
inconsistency, particularly wrt the forced public constructor. This
does concern me, and I worry the feedback once from the wider
community may include a fair few complaints.
Where records might be useful in our codebase is for classes that
currently extend TypedString -
https://github.com/OpenGamma/Strata/blob/master/modules/collect/src/main/java/com/opengamma/strata/collect/TypedString.java#L16
but this is not a major gain for us, particularly as we'd be forced to
use a public constructor which we'd view as a step backwards. I can
also see use cases where the ease of creating a record might cause
types to be created more often. But I struggle to see any use cases
where there are more then 2 or 3 fields in a record because of the
public constructor, lack of factory and lack of builder.
Reading the spec:
- The mental model of "restricting a class", similar to how an Enum
operates, feels like a good approach.
- I agree with making records final, being static when nested, and not
having an instance initializer.
- I agree with making records immutable, subject to my concerns about
creation and modification when there are more than a few fields.
- I agree with implicit accessors being foo() and not getFoo().
- the serialization/deserialization story looks good.
- I understand the need to prevent records from having other fields,
however I do feel that some form of cached/lazy field or evaluate once
method is going to be needed.
- I suspect that the ability to override equals/hashCode/toString will
lead to subtle puzzlers/bugs. I'm not sure there is a viable
alternative however, as there are bound to be some valid use cases for
overriding the defaults.
- The compact constructor looks to be a reasonable solution to adding
validation. However, it is not declarative thus cannot be queried
using framework code - something that is useful when building a
serialization framework.
- I'm not a big fan of the terminology "record components". The word
"component" is widely used in Java, but generally has a meaning for
larger parts of a system. I don't recall how this name came about, but
a different name would definitely be preferable (why not properties?).
- I'm entirely unconvinced that there is any need for vararg record
components. Varargs have their uses, but records would IMO be better
off without varargs, particularly as they encourage use of arrays
which have many other issues in the context of records. Varargs could
always be added later, but can never be taken away. Please leave
varags out.
- I find the requirement to have a public constructor undesirable,
although I understand the rationale. Having spent years avoiding
constructors, I don't want to go back to writing `new` everywhere.
This may well need "factory as a language feature", but such as thing
would surely be a good thing. Even allowing Foo.new() would be a step
forward.
My biggest complaint however is the syntax, and I'm not sure the
expert group has ever properly considered options here.
A record is very much like an Enum. It takes a class and restricts it.
Yet the proposal uses a completely different syntax, one that is used
in other languages, but is IMO not consistent with the history of
Java.
Enums could have had a syntax like this:
public enum TrafficLight(RED, YELLOW, GREEN) {
// other code
}
But, this route was not taken. Partly this was the need for the
constants to have associated code (enum subclasses). But I believe it
was mostly because Java already puts fields in the main class body -
both static and instance.
With records, I believe the current syntax does not scale well beyond
2 or 3 fields. Adding annotations to each field, or Javadoc creates an
unattractive syntactic mess IMO where the fields have to be spread
onto multiple lines. I don't personally believe the concept of
"parameters to the class" is a helpful one.
I believe that the history with Enum suggests that Java should not
follow Kotlin and Scala, but adopt a syntax similar to Enums:
public record DocumentKey {
String id,
int version;
// other code
}
Record fields would immediately follow the open brace and be comma
separated exactly as enums are. A semicolon would end the declaration
section and begin the rest of the code. Such an approach gains
consistency with enums, and provides a more natural place for
annotations and Javadoc.
But the syntax is verbose some might cry! Well for the simple case it
is actually shorter and scales exactly like enums:
public enum TrafficLight {RED, YELLOW, GREEN}
public record DocumentKey {String id, int version}
And for the non-simple case, it is behaving more like a class anyway,
so using multiple lines is entirely appropriate.
I firmly believe that defining fields should be consistent across
Java, from class to enum to record - declaring in the class header
doesn't do that.
thanks
Stephen
More information about the amber-dev
mailing list