Records and annotations (was: Updated document on data classes and sealed types)

Brian Goetz brian.goetz at oracle.com
Sat Mar 9 13:57:47 UTC 2019


This raises a related question, which is: what if the author likes the default implementation, but wants to add more annotations (or Javadoc)?  

Currently, the story is decent for constructors; declaring an empty `Foo { }` constructor recreates the default behavior.  There is no equivalent for accessors, equals, hashCode, or toString.  We toyed with a “default.m()” syntax in the past:

     record R(int x, int y) { 
          @MyAnnotation
          public boolean equals(Object o) -> default.equals(o);
     }

which doesn’t seem so bad, and is surely better than trying (and maybe failing) to reproduce the default behavior imperatively.  


> On Mar 9, 2019, at 12:47 PM, Brian Goetz <brian.goetz at oracle.com> wrote:
> 
> This came up before, but we didn’t reach a conclusion.  
> 
> A record component is more than just the lower-level members (fields, accessors, ctor params) it gets desugared too.  So it seems reasonable that it be considered an annotatable program element, and that reflection expose directly the annotations on record components (separately from any annotations on the class members that may or may not derive from desugaring of records.)  
> 
> But, that still leaves the question of whether the desugaring should, or should not be, transparent to annotations.  My sense is that pushing annotations down to fields, ctor params, and accessors _seems_ friendly, but also opens a number of uncomfortable questions.  
> 
>  - Should we treat the cases where @A has a target of RECORD_COMPONENT, separately from the cases where it does not, such as, only push the annotation down to members when the target does not include RECORD_COMPONENT?  That is, is the desire to push down annotations based on “well, what if we want to apply a “legacy” annotation?  If so, this causes a migration compatibility issue; if someone adds RC to the targets list for @A, then when the record is recompiled, the location of the annotations will changed, possibly changing the behavior of frameworks that encounter the record.  
> 
>  - What if @A has a target set of { field, parameter }, but for some reason the user does _not_ want the annotation pushed down?  Tough luck?   Redeclare the member without the annotation?  
> 
>  - If the user explicitly redeclares the member (ctor, accessor), what happens?  Do we still implicitly push down annotations from record components to the explicit member?  Will this be confusing when the source says “@B int x() -> x”, but reflection yields both @A and @B as annotations on x()?  
> 
> All of which causes me to back up and say: what is the motivation for pushing these down to implicit members, other than “general friendliness”?   Is this a migration strategy for migrating existing code to use records, without having to redeclare annotations on the members?  And if so, how useful is it really?  Will users want to throw the union of field/accessor/ctor parameter annotations on the record components just to gain compatibility with their existing code?  
> 
> My gut sense is that the stable solution is to make record component a new kind of target, and encourage frameworks to learn about these, rather than trying to fake out frameworks by emulating legacy behavior.  
> 
> 
>> On Mar 8, 2019, at 8:43 PM, Kevin Bourrillion <kevinb at google.com <mailto:kevinb at google.com>> wrote:
>> 
>> Re: annotations,
>> 
>> Doc says, "Record components constitute a new place to put annotations; we'll likely want to extend the @Target meta-annotation to reflect this."
>> 
>> I'm sure we discussed this before, but I also expect to be able to put any METHOD-, FIELD- or PARAMETER-targeted annotation on a record component, and have that annotation appear to be present on the synthesized accessor/field/constructor-parameter. Is that sensible?
>> 
>> (As for records themselves, I expect they are targeted with TYPE just as enums/interfaces/"plain old classes" (jeesh, is there any term that means the latter?).)
>> 
>> 
>> 
>> 
>> 
>> 
>> 
>> On Fri, Mar 1, 2019 at 12:16 PM Brian Goetz <brian.goetz at oracle.com <mailto:brian.goetz at oracle.com>> wrote:
>> I've updated the document on data classes here:
>> 
>>      http://cr.openjdk.java.net/~briangoetz/amber/datum.html <http://cr.openjdk.java.net/~briangoetz/amber/datum.html>
>> 
>> (older versions of the document are retained in the same directory for 
>> historical comparison.)
>> 
>> While the previous version was mostly about tradeoffs, this version 
>> takes a much more opinionated interpretation of the feature, offering 
>> more examples of use cases of where it is intended to be used (and not 
>> used).  Many of the "under consideration" flexibilities (extension, 
>> mutability, additional fields) have collapsed to their more restrictive 
>> form; while some people will be disappointed because it doesn't solve 
>> the worst of their boilerplate problems, our conclusion is: records are 
>> a powerful feature, but they're not necessarily the delivery vehicle for 
>> easing all the (often self-inflicted) pain of JavaBeans.  We can 
>> continue to explore relief for these situations too as separate 
>> features, but trying to be all things to all classes has delayed the 
>> records train long enough, and I'm convince they're separate problems 
>> that want separate solutions.  Time to let the records train roll.
>> 
>> I've also combined the information on sealed types in this document, as 
>> the two are so tightly related.
>> 
>> Comments welcome.
>> 
>> 
>> -- 
>> Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com <mailto:kevinb at google.com>

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.java.net/pipermail/amber-spec-experts/attachments/20190309/fa5ce967/attachment-0001.html>


More information about the amber-spec-experts mailing list