Records -- current status

Kevin Bourrillion kevinb at google.com
Wed Mar 21 21:59:41 UTC 2018


On Tue, Mar 20, 2018 at 6:57 AM, Federico Peralta Schaffner <
federico.peralta at gmail.com> wrote:

Besides this, I think that copy constructors are widely used (much more
> than Cloneable and .clone()), which makes me think about 2 things:
>

imho, copy constructors are useful to support heterogeneous copies (e.g.
`new HashMap<>(aTreeMap)`), which we have no need of, and that's about it.
Aside from that, instance methods are the clearly conventional way to do
things like this, and we should call it "clone" because that's the
convention.

That said, I'm getting more worried about the idea of the clone method on
records. A regular Object doesn't automatically get clone() unless it asks
for it, because we don't know how deep it expects that clone to be. By
making it opt in, we have more reason to assume the user probably thought
about that. What really makes records different here? We could continue to
require at least an explicit `implements Cloneable`?


> - Additional instance fields. These are a much bigger concern. While the
> primary arguments against them are of the "slippery slope" variety, I still
> have deep misgivings about supporting unrestricted non-principal instance
> fields, and I also haven't found a reasonable set of restrictions that
> makes this less risky. I'd like to keep looking for a better story here,
> before just caving on this, as I worry doing so will end up biting us in
> the back.
>
> I think that additional fields can be very dangerous for the little benefit
> they might bring to records. In fact, I don't see a real value for them.
> Suppose you have:
>
>     record Name(String first, String last) {
>         public firstAndLast() { return first + " " + last; }
>     }
>
> Why caching the result of first + " " + last in an additional firstAndLast
> instance field? For records, it should be enough to expose derived state
> via accessor methods. If you want to cache some value (because it's
> expensive to calculate), you can always resort to classes, or could maybe
> use another record. For the example above, you could have:
>
>     record FullName(Name name, String fullName) {
>         @Override
>         public FullName(Name name, String fullName) {
>             if (!name.firstAndLast().equals(fullName)) {
>                 throw new IllegalArgumentException();
>             }
>             default.this(name, name.firstAndLast());
>         }
>
>         public FullName(Name name) {
>             this(name, name.firstAndLast()); // delegates to the overriden
> default constructor
>         }
>     }
>

I would have described this as an example of the kind of unfortunate
complexity users would go to without support for extra derived fields;
i.e., as an argument why we *should* allow them. And it requires eager
initialization, and it unnecessarily checks fullName in equals() and
hashCode().

I continue to seek an understanding of what our basis for forbidding these
fields really is. They seem harmless to me. Some things make sense to
lazily initialize.


But we need to support the uniform access principle... Really? Do we? Why?
>

We don't *need* to, but it's still nice to. I don't want developers to have
to keep track of whether they use the parens or not on this class vs. that
class. Just always use them. The fact that one *can* make terrible design
decisions and do massive work in a method that looks like an accessor is
beside the point I think.


-- 
Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com


More information about the amber-dev mailing list