Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8238240

java.lang.Record spec clarifications

    XMLWordPrintable

    Details

    • Type: CSR
    • Status: Closed
    • Priority: P3
    • Resolution: Approved
    • Fix Version/s: 15
    • Component/s: core-libs
    • Labels:
    • Subcomponent:
    • Compatibility Kind:
      behavioral
    • Compatibility Risk:
      minimal
    • Compatibility Risk Description:
      Records have just recently being released. There shouldn't be a lot of user's code depending on it. Also records are still in preview meaning that it can change or be completely removed in future releases

      Description

      Summary

      The specification of the three methods from java.lang.Object overridden in java.lang.Record's should be clarified. In particular the specification shouldn't mention any method and it should be specified in some cases the relation between the output of some methods for records obtained from the same components.

      Problem

      The current specification for method java.lang.Record::equals refers to methods like java.util.Objects::equals, this could be read as an obligation for a given implementation to invoke the mentioned methods. Although the current specification suggest that there is no obligation to invoke the mentioned methods, this should be indicated more explicitly. Also the current specification doesn't refer to the order in which the components are compared. It should be stated that the specification defines no restriction on the comparison order.

      The current specification for java.lang.Record::hashCode shouldn't impose that all the components should participate in the hash calculation, there could be use cases for which more flexibility could be needed. On the other hand, the specification doesn't enforce any constraint on the relation between the hashes of records created from the same components. There is also no mention to the consistency of the hash value between different executions of the application.

      Regarding method java.lang.Record::toString, the specification doesn't make any mention to the relation between the string representations of records created from the same components. Also it shouldn't refer to any method to obtain the string representation of a particular component, as this could be interpreted as a mandate to use a particular method.

      Solution

      The specification for java.lang.Record::equals should state clearly that even though some methods are mentioned in the @implSpec section, to explain the semantics of the method, no commitment is made that those methods must be invoked by a particular implementation. At the same time, it should be stated that the order in which the record components are compared may or may not be the same as the order of their declaration.

      The specification for java.lang.Record::hashCode should state that if two records are created from the same components, then they must have the same hash code. Apart from these, the specification must not impose that all the components should participate in the computation of the final result. It should also be specified that the computed hash value doesn't need to be consistent between different executions of the application. There shouldn't be any rule about how the hash should be calculated for primitive or reference types

      Finally for the case of java.lang.Record::toString, the specification should state that if two records are equal, then they must produce the same string representation. The specification shouldn't mention any method that must be used to obtain a string representation of a particular record component. Also it should state that the output of the method can change and thus shouldn't be parsed by applications to recover record component values.

      Specification

      --- old/src/java.base/share/classes/java/lang/Record.java   2020-01-30 11:11:32.525012992 -0800
      +++ new/src/java.base/share/classes/java/lang/Record.java   2020-01-30 11:11:32.204699400 -0800
      @@ -109,7 +109,7 @@
            * @implSpec
            * The implicitly provided implementation returns {@code true} if
            * and only if the argument is an instance of the same record type
      -     * as this object, and each component of this record is equal to
      +     * as this record, and each component of this record is equal to
            * the corresponding component of the argument; otherwise, {@code
            * false} is returned. Equality of a component {@code c} is
            * determined as follows:
      @@ -130,46 +130,70 @@
            *
            * </ul>
            *
      -     * The implicitly provided implementation conforms to the
      -     * semantics described above; the implementation may or may not
      -     * accomplish this by using calls to the particular methods
      -     * listed.
      +     * Apart from the semantics described above, the precise algorithm
      +     * used in the implicitly provided implementation is unspecified
      +     * and is subject to change. The implementation may or may not use
      +     * calls to the particular methods listed, and may or may not
      +     * perform comparisons in the order of component declaration.
            *
            * @see java.util.Objects#equals(Object,Object)
            *
            * @param   obj   the reference object with which to compare.
      -     * @return  {@code true} if this object is equal to the
      +     * @return  {@code true} if this record is equal to the
            *          argument; {@code false} otherwise.
            */
           @Override
           public abstract boolean equals(Object obj);
      
           /**
      +     * Returns a hash code value for the record.
            * Obeys the general contract of {@link Object#hashCode Object.hashCode}.
      +     * For records, hashing behavior is constrained by the refined contract
      +     * of {@link Record#equals Record.equals}, so that any two records
      +     * created from the same components must have the same hash code.
            *
            * @implSpec
            * The implicitly provided implementation returns a hash code value derived
      -     * by combining the hash code value for all the components, according to
      -     * {@link Object#hashCode()} for components whose types are reference types,
      -     * or the primitive wrapper hash code for components whose types are primitive
      -     * types.
      +     * by combining appropriate hashes from each component.
      +     * The precise algorithm used in the implicitly provided implementation
      +     * is unspecified and is subject to change within the above limits.
      +     * The resulting integer need not remain consistent from one
      +     * execution of an application to another execution of the same
      +     * application, even if the hashes of the component values were to
      +     * remain consistent in this way.  Also, a component of primitive
      +     * type may contribute its bits to the hash code differently than
      +     * the {@code hashCode} of its primitive wrapper class.
            *
            * @see     Object#hashCode()
            *
      -     * @return  a hash code value for this object.
      +     * @return  a hash code value for this record.
            */
           @Override
           public abstract int hashCode();
      
           /**
      -     * Obeys the general contract of {@link Object#toString Object.toString}.
      +     * Returns a string representation of the record.
      +     * In accordance with the general contract of {@link Object#toString()},
      +     * the {@code toString} method returns a string that
      +     * "textually represents" this record. The result should
      +     * be a concise but informative representation that is easy for a
      +     * person to read.
      +     * <p>
      +     * In addition to this general contract, record classes must further
      +     * participate in the invariant that any two records which are
      +     * {@linkplain Record#equals(Object) equal} must produce equal
      +     * strings.  This invariant is necessarily relaxed in the rare
      +     * case where corresponding equal component values might fail
      +     * to produce equal strings for themselves.
            *
            * @implSpec
      -     * The implicitly provided implementation returns a string that is derived
      -     * from the name of the record class and the names and string representations
      -     * of all the components, according to {@link Object#toString()} for components
      -     * whose types are reference types, and the primitive wrapper {@code toString}
      -     * method for components whose types are primitive types.
      +     * The implicitly provided implementation returns a string which
      +     * contains the name of the record class, the names of components
      +     * of the record, and string representations of component values,
      +     * so as to fulfill the contract of this method.
      +     * The precise format produced by this implicitly provided implementation
      +     * is subject to change, so the present syntax should not be parsed
      +     * by applications to recover record component values.
            *
            * @see     Object#toString()
            *

        Attachments

          Issue Links

            Activity

              People

              Assignee:
              vromero Vicente Arturo Romero Zaldivar
              Reporter:
              vromero Vicente Arturo Romero Zaldivar
              Reviewed By:
              Leonid Kuskov, Paul Sandoz
              Votes:
              0 Vote for this issue
              Watchers:
              2 Start watching this issue

                Dates

                Created:
                Updated:
                Resolved: