Feedback for records: Accessors name() vs. getName()

Kamil Ševeček kamil at sevecek.net
Wed Aug 5 20:01:27 UTC 2020


Hi Brian

I take all your arguments about "just" naming conventions of getName().
On the other hand, I was unable to come with a single reason why name()
would be better than getName().
Can you give an example, please?

Regards
Kamil Sevecek

On Wed, 5 Aug 2020 at 15:43, Brian Goetz <brian.goetz at oracle.com> wrote:

> Thanks for the feedback.  Some comments inline.
>
> The good news is that most of your concerns are transient; frameworks (and
> there are only a few of them) will have to update to recognize the new
> conventions (and it's easy for them to do so.)  We expect, within a year of
> or so of records coming out, that most frameworks will have figured this
> out.  One of the reasons to do preview features is so that frameworks can
> see these things coming; I expect, for example, that Spring is likely to be
> record-ready by the time records are a permanent feature of the language.
>
> 1. java.beans.Introspector not recognizing accessors
>
>
> This one was raised on this list before; there's no reason Introspector
> can't learn about records -- all the reflective machinery is there to do
> so.  Someone at one point offered to contribute this change, but so far I
> haven't seen it.  We would be happy to take back such a patch.
>
> I was able to find this email (https://mail.openjdk.java.net/pipermail/amber-dev/2020-January/005479.html)
> regarding the same issue but the only thing I could see was a proposal to
> shoehorn Introspector to recognize records instead of making records
> standards-compliant and not having changing anything in the APIs above
> java.lang.
>
>
> Most of the arguments about accessor naming seem to stem from the same bit
> of wishful thinking -- that the getX/setX (and don't forget about isX)
> convention is somehow some sort of "standard" (if it were, it would be a
> terrible one -- we tend to forget how much pain frameworks go through to
> try to recover design intent from naming conventions, because most of this
> code was written 22 years ago.)  But, this is simply not the case; it is
> merely a convention.  As language stewards, we have to balance what is good
> for the short- and long-term benefit of the ecosystem.  We surely did
> consider whether it was worth adopting this convention for the sake of
> short-term migration, but in the end, we decided that would be "doing the
> wrong thing" for the long term.
>
> As I said, most of these have a relatively short-term solution -- we just
> have to remind maintainers of frameworks that the language has evolved, and
> that they need to do some work to evolve with it.  And it's not a lot of
> work.
>
> 2. Unified expression language (EL) in JSP, JSF
> --------------------------------------------------------------
> Web expression language will look quite weird as well (address() method
> versus customer and city properties):
>
>
> Again, there's no reason why EL can't treat record component accessors the
> same way as methods who happen to be named g-e-t-something.  And there's
> no reason why the () would be needed; it's just that they're not caught up
> with the language yet.
>
> We encourage you to request that the maintainers of EL add this to their
> bag of magic tricks; it's not hard.
>
> 3. Spring Framework hacks, Spring (SpEL)
>
>
> Same story.  I'll bet, though, that Spring is already on top of this.
>
> 3. Groovy inconsistency
> -----------------------
> You can access getName() getters in Groovy using short syntax:
>
>
> Same.
>
> 4. Kotlin inconsistency
> -----------------------
> The same as in Groovy also applies to Kotlin:
>
>
> Same, and again, I'll bet they're already on top of it.
>
> 5. All libraries will have to be adapted
> ----------------------------------------
> There are a lot of libraries that depend on the get/set convention and you
> would prevent their usage for records. Such as BeanTableModel (swing
> TableModel implementation), Jackson JSON parser and many others.
>
>
> Yes, there will be some cost for the ecosystem to adapt -- but I think its
> less than you're making it out to be -- and less about the naming that you
> might think.  There are plenty of these libraries, but it's not thousands
> -- I'd bet a few tens would probably cover 97% of uses.  And the cost for
> any individual library is literally a few hours work (its actually less
> code to find that a class is a record and that x() is a component accessor
> than, say, to determine that setX() and getX() form a matched pair.)  This
> code is, essentially:
>
>     if (clazz.isRecord()) {
>         Stream.of(clazz.getRecordComponents())
>                      .forEach(c -> addReadOnlyAccessor(clazz, c.getName(),
> c.getAccessor()));
>     }
>
> where `addReadOnlyAccessor` is whatever you do to add the mapping that
> "component N of class C is accessed by method M".  All these frameworks
> work by building a "symbol table" indexed by receiver class and component
> name, and map to some description that bottoms out in a java.lang.Method
> (which is what RecordComponent::getAccessor returns.)  This is much less
> work than walking inheritance chains to find methods that conform to a set
> of ad-hoc naming conventions, and correlating similarly-named methods to
> infer the presence of a synthetic property.
>
> Further, more of the cost here is not about the naming convention, but the
> fact that frameworks that reconstruct objects reflectively are used to
> calling a no-arg constructor, and then calling setters.  That's not an
> option with records, regardless of the naming convention chosen for
> accessors.   So Jackson is going to have to adapt no matter what -- and the
> adaptation to immutable objects is far more work than the adaptation to a
> new naming scheme (which has reflection support, see above.)
>
> 6. Impossibility to retrofit existing record-like classes as records
>
>
> This one is different from the others, as it speaks not to "it will take a
> little time for the ecosystem to catch up to the language", but about what
> ordinary users have to do.  And, this speaks to a design tension we
> wrestled with quite a bit.
>
> The tension is, basically, old code vs new.  Obviously, if its possible,
> we want for a new feature to work great for both old AND new code.  But
> where there is a conflict between "the way things have been done", and how
> they might be done better in the future, there is a tension.
>
> In records, we made the deliberate choice to design the feature for new
> code than for catering to the quirks of existing code.  (With something as
> quirky as the JavaBean naming conventions, we could never be
> quirk-for-quirk compatible anyway; consider the getX vs isX issue, which is
> inconsistently applied throughout the ecosystem, because, well, it's a
> convention, not a standard.)   The model for records (immutable,
> transparent carriers for a fixed state vector) is different enough from
> mutable JavaBeans that, even if we had made this concession to legacy, I
> don't think people would have been much happier.
>
> Also, the claim that it is "impossible" to retrofit existing record-like
> classes is vastly overstated.  If you have a class that meets the
> requirements to be a record (representation == construction protocol ==
> access protocol == equals/hashCode/toString protocols), you can surely
> migrate it to a record, and then, if you have to for legacy reasons,
> declare some old-style getters to delegate to the new ones.  That is far
> from "impossible" -- and was considered in the design (same for having
> additional constructors.)  I think what you mean is that then you don't get
> to write a one-line class declaration?  And that's true -- because you've
> got a dependency on a framework that uses information from outside the type
> system to infer the semantics of your class.  So you pay a few-line "tax"
> for migrating these classes to records.  So yes, new code will pay less tax
> than old code, but its not the case you can't migrate to records and get
> some benefit.  It's just that if you have extra requirements, you have to
> write some extra code.  (Further, even this is likely to be transient; once
> frameworks catch up, the need for these "bridge" accessors would go away,
> and you can delete them.)
>
> I realize its easy to jump from "we've been doing it this way for 22
> years" to "this is the One True Absolutely Right Way To Do It Forever"
> without realizing it, but the language has a responsibility to look forward
> as well as backward, and balance the needs of existing code with the needs
> of new code.  Taking a bad library naming convention and burning it into
> the language forever would have been the worse choice.
>
> Cheers,
> -Brian
>
>


More information about the amber-dev mailing list