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

Augment discussion of equivalence classes in Object.equals and comparison methods

    XMLWordPrintable

    Details

    • Type: CSR
    • Status: Closed
    • Priority: P4
    • Resolution: Approved
    • Fix Version/s: 17
    • Component/s: core-libs
    • Labels:
      None
    • Subcomponent:
    • Compatibility Risk:
      minimal
    • Interface Kind:
      Java API
    • Scope:
      SE

      Description

      Summary

      Augment the discussion of requirements for equals and compareTo methods, including an update to BigDecimal, whose natural order is not consistent with equals.

      Problem

      The related requirements for equals and compareTo methods is not necessarily clearly explained in the relevant javadoc.

      Solution

      Update the specs of Object.equals, java.lang.Comparable, java.math.BigDecimal, etc. to better explain the relationships at hand. Use @implSpec, @apiNote, and related tags in Object and elsewhere. These new tags are not spec changes per se, but will affect what text gets @inheritDoc'ed by subclasses.

      Specification

      diff --git a/src/java.base/share/classes/java/lang/Comparable.java b/src/java.base/share/classes/java/lang/Comparable.java
      index 0b5075c5e5f..eede91e30e2 100644
      --- a/src/java.base/share/classes/java/lang/Comparable.java
      +++ b/src/java.base/share/classes/java/lang/Comparable.java
      @@ -64,9 +64,12 @@
        *
        * Virtually all Java core classes that implement {@code Comparable} have natural
        * orderings that are consistent with equals.  One exception is
      - * {@code java.math.BigDecimal}, whose natural ordering equates
      - * {@code BigDecimal} objects with equal values and different precisions
      - * (such as 4.0 and 4.00).<p>
      + * {@link java.math.BigDecimal}, whose {@linkplain java.math.BigDecimal#compareTo natural ordering} equates
      + * {@code BigDecimal} objects with equal numerical values and different representations
      + * (such as 4.0 and 4.00). For {@link java.math.BigDecimal#equals
      + * BigDecimal.equals()} to return true, the representation and
      + * numerical value of the two {@code BigDecimal} objects must be the
      + * same.<p>
        *
        * For the mathematically inclined, the <i>relation</i> that defines
        * the natural ordering on a given class C is:<pre>{@code
      @@ -83,7 +86,12 @@
        * the class's {@link Object#equals(Object) equals(Object)} method:<pre>
        *     {(x, y) such that x.equals(y)}. </pre><p>
        *
      - * This interface is a member of the
      + * In other words, when a class's natural ordering is consistent with
      + * equals, the equivalence classes defined by the equivalence relation
      + * of the {@code equals} method and the equivalence classes defined by
      + * the quotient of the {@code compareTo} method are the same.
      + *
      + * <p>This interface is a member of the
        * <a href="{@docRoot}/java.base/java/util/package-summary.html#CollectionsFramework">
        * Java Collections Framework</a>.
        *
      @@ -100,9 +108,9 @@
            * than, equal to, or greater than the specified object.
            *
            * <p>The implementor must ensure
      -     * {@code sgn(x.compareTo(y)) == -sgn(y.compareTo(x))}
      +     * {@link Integer#signum signum}{@code (x.compareTo(y)) == -signum(y.compareTo(x))}
            * for all {@code x} and {@code y}.  (This
      -     * implies that {@code x.compareTo(y)} must throw an exception iff
      +     * implies that {@code x.compareTo(y)} must throw an exception if and only if
            * {@code y.compareTo(x)} throws an exception.)
            *
            * <p>The implementor must also ensure that the relation is transitive:
      @@ -110,22 +118,17 @@
            * {@code x.compareTo(z) > 0}.
            *
            * <p>Finally, the implementor must ensure that {@code x.compareTo(y)==0}
      -     * implies that {@code sgn(x.compareTo(z)) == sgn(y.compareTo(z))}, for
      +     * implies that {@code signum(x.compareTo(z)) == signum(y.compareTo(z))}, for
            * all {@code z}.
            *
      -     * <p>It is strongly recommended, but <i>not</i> strictly required that
      +     * @apiNote
      +     * It is strongly recommended, but <i>not</i> strictly required that
            * {@code (x.compareTo(y)==0) == (x.equals(y))}.  Generally speaking, any
            * class that implements the {@code Comparable} interface and violates
            * this condition should clearly indicate this fact.  The recommended
            * language is "Note: this class has a natural ordering that is
            * inconsistent with equals."
            *
      -     * <p>In the foregoing description, the notation
      -     * {@code sgn(}<i>expression</i>{@code )} designates the mathematical
      -     * <i>signum</i> function, which is defined to return one of {@code -1},
      -     * {@code 0}, or {@code 1} according to whether the value of
      -     * <i>expression</i> is negative, zero, or positive, respectively.
      -     *
            * @param   o the object to be compared.
            * @return  a negative integer, zero, or a positive integer as this object
            *          is less than, equal to, or greater than the specified object.
      diff --git a/src/java.base/share/classes/java/lang/Object.java b/src/java.base/share/classes/java/lang/Object.java
      index a155e1e8ba8..ad09e6bd5d6 100644
      --- a/src/java.base/share/classes/java/lang/Object.java
      +++ b/src/java.base/share/classes/java/lang/Object.java
      @@ -78,11 +78,11 @@ public Object() {}
            *     used in {@code equals} comparisons on the object is modified.
            *     This integer need not remain consistent from one execution of an
            *     application to another execution of the same application.
      -     * <li>If two objects are equal according to the {@code equals(Object)}
      +     * <li>If two objects are equal according to the {@link equals(Object) equals}
            *     method, then calling the {@code hashCode} method on each of
            *     the two objects must produce the same integer result.
            * <li>It is <em>not</em> required that if two objects are unequal
      -     *     according to the {@link java.lang.Object#equals(java.lang.Object)}
      +     *     according to the {@link equals(Object) equals}
            *     method, then calling the {@code hashCode} method on each of the
            *     two objects must produce distinct integer results.  However, the
            *     programmer should be aware that producing distinct integer results
      @@ -127,15 +127,26 @@ public Object() {}
            * <li>For any non-null reference value {@code x},
            *     {@code x.equals(null)} should return {@code false}.
            * </ul>
      +     *
            * <p>
      +     * An equivalence relation partitions the elements it operates on
      +     * into <i>equivalence classes</i>; all the members of an
      +     * equivalence class are equal to each other. Members of an equivalence class
      +     * are substitutable for each other, at least for some purposes.
      +     *
      +     * @implSpec
            * The {@code equals} method for class {@code Object} implements
            * the most discriminating possible equivalence relation on objects;
            * that is, for any non-null reference values {@code x} and
            * {@code y}, this method returns {@code true} if and only
            * if {@code x} and {@code y} refer to the same object
            * ({@code x == y} has the value {@code true}).
      -     * <p>
      -     * Note that it is generally necessary to override the {@code hashCode}
      +     *
      +     * In other words, under the reference equality equivalence
      +     * relation, each equivalence class only has a single element.
      +     *
      +     * @apiNote
      +     * It is generally necessary to override the {@link hashCode hashCode}
            * method whenever this method is overridden, so as to maintain the
            * general contract for the {@code hashCode} method, which states
            * that equal objects must have equal hash codes.
      @@ -183,7 +194,8 @@ public boolean equals(Object obj) {
            * primitive fields or references to immutable objects, then it is usually
            * the case that no fields in the object returned by {@code super.clone}
            * need to be modified.
      -     * <p>
      +     *
      +     * @implSpec
            * The method {@code clone} for class {@code Object} performs a
            * specific cloning operation. First, if the class of this object does
            * not implement the interface {@code Cloneable}, then a
      @@ -214,13 +226,16 @@ public boolean equals(Object obj) {
           protected native Object clone() throws CloneNotSupportedException;
      
           /**
      -     * Returns a string representation of the object. In general, the
      +     * Returns a string representation of the object.
      +     * @apiNote
      +     * In general, the
            * {@code toString} method returns a string that
            * "textually represents" this object. The result should
            * be a concise but informative representation that is easy for a
            * person to read.
            * It is recommended that all subclasses override this method.
      -     * <p>
      +     * The string output is not necessarily stable over time or across JVM invocations.
      +     * @implSpec
            * The {@code toString} method for class {@code Object}
            * returns a string consisting of the name of the class of which the
            * object is an instance, the at-sign character `{@code @}', and
      diff --git a/src/java.base/share/classes/java/math/BigDecimal.java b/src/java.base/share/classes/java/math/BigDecimal.java
      index bb698a555b6..b4e28e6697c 100644
      --- a/src/java.base/share/classes/java/math/BigDecimal.java
      +++ b/src/java.base/share/classes/java/math/BigDecimal.java
      @@ -63,8 +63,7 @@
        * <p>When a {@code MathContext} object is supplied with a precision
        * setting of 0 (for example, {@link MathContext#UNLIMITED}),
        * arithmetic operations are exact, as are the arithmetic methods
      - * which take no {@code MathContext} object.  (This is the only
      - * behavior that was supported in releases prior to 5.)  As a
      + * which take no {@code MathContext} object. As a
        * corollary of computing the exact result, the rounding mode setting
        * of a {@code MathContext} object with a precision setting of 0 is
        * not used and thus irrelevant.  In the case of divide, the exact
      @@ -79,9 +78,8 @@
        * {@code BigDecimal} arithmetic are broadly compatible with selected
        * modes of operation of the arithmetic defined in ANSI X3.274-1996
        * and ANSI X3.274-1996/AM 1-2000 (section 7.4).  Unlike those
      - * standards, {@code BigDecimal} includes many rounding modes, which
      - * were mandatory for division in {@code BigDecimal} releases prior
      - * to 5.  Any conflicts between these ANSI standards and the
      + * standards, {@code BigDecimal} includes many rounding modes.
      + * Any conflicts between these ANSI standards and the
        * {@code BigDecimal} specification are resolved in favor of
        * {@code BigDecimal}.
        *
      @@ -90,6 +88,15 @@
        * and rounding must specify both the numerical result and the scale
        * used in the result's representation.
        *
      + * The different representations of the same numerical value are
      + * called members of the same <i>cohort</i>. The {@linkplain
      + * compareTo(BigDecimal) natural order} of {@code BigDecimal}
      + * considers members of the same cohort to be equal to each other. In
      + * contrast, the {@link equals equals} method requires both the
      + * numerical value and representation to be the same for equality to
      + * hold. The results of methods like {@link scale} and {@link
      + * unscaledValue} will differ for numerically equal values with
      + * different representations.
        *
        * <p>In general the rounding modes and precision setting determine
        * how operations return results with a limited number of digits when
      @@ -3040,16 +3047,21 @@ public BigDecimal stripTrailingZeros() {
           // Comparison Operations
      
           /**
      -     * Compares this {@code BigDecimal} with the specified
      +     * Compares this {@code BigDecimal} numerically with the specified
            * {@code BigDecimal}.  Two {@code BigDecimal} objects that are
            * equal in value but have a different scale (like 2.0 and 2.00)
      -     * are considered equal by this method.  This method is provided
      +     * are considered equal by this method. Such values are in the same <i>cohort</i>.
      +     *
      +     * This method is provided
            * in preference to individual methods for each of the six boolean
            * comparison operators ({@literal <}, ==,
            * {@literal >}, {@literal >=}, !=, {@literal <=}).  The
            * suggested idiom for performing these comparisons is:
            * {@code (x.compareTo(y)} &lt;<i>op</i>&gt; {@code 0)}, where
            * &lt;<i>op</i>&gt; is one of the six comparison operators.
      +
      +     * @apiNote
      +     * Note: this class has a natural ordering that is inconsistent with equals.
            *
            * @param  val {@code BigDecimal} to which this {@code BigDecimal} is
            *         to be compared.
      @@ -3129,8 +3141,8 @@ else if (ys != INFLATED)
            * {@code Object} for equality.  Unlike {@link
            * #compareTo(BigDecimal) compareTo}, this method considers two
            * {@code BigDecimal} objects equal only if they are equal in
      -     * value and scale (thus 2.0 is not equal to 2.00 when compared by
      -     * this method).
      +     * value and scale. Therefore 2.0 is not equal to 2.00 when compared by
      +     * this method.
            *
            * @param  x {@code Object} to which this {@code BigDecimal} is
            *         to be compared.
      diff --git a/src/java.base/share/classes/java/util/Comparator.java b/src/java.base/share/classes/java/util/Comparator.java
      index d009807a75a..b1b445bf55a 100644
      --- a/src/java.base/share/classes/java/util/Comparator.java
      +++ b/src/java.base/share/classes/java/util/Comparator.java
      @@ -38,9 +38,9 @@
        * as {@link Collections#sort(List,Comparator) Collections.sort} or {@link
        * Arrays#sort(Object[],Comparator) Arrays.sort}) to allow precise control
        * over the sort order.  Comparators can also be used to control the order of
      - * certain data structures (such as {@link SortedSet sorted sets} or {@link
      + * certain data structures (such as {@linkplain SortedSet sorted sets} or {@linkplain
        * SortedMap sorted maps}), or to provide an ordering for collections of
      - * objects that don't have a {@link Comparable natural ordering}.<p>
      + * objects that don't have a {@linkplain Comparable natural ordering}.<p>
        *
        * The ordering imposed by a comparator {@code c} on a set of elements
        * {@code S} is said to be <i>consistent with equals</i> if and only if
      @@ -89,6 +89,11 @@
        * equals(Object)} method(s):<pre>
        *     {(x, y) such that x.equals(y)}. </pre>
        *
      + * In other words, when the imposed ordering is consistent with
      + * equals, the equivalence classes defined by the equivalence relation
      + * of the {@code equals} method and the equivalence classes defined by
      + * the quotient of the {@code compare} method are the same.
      +
        * <p>Unlike {@code Comparable}, a comparator may optionally permit
        * comparison of null arguments, while maintaining the requirements for
        * an equivalence relation.
      @@ -112,8 +117,8 @@
            * zero, or a positive integer as the first argument is less than, equal
            * to, or greater than the second.<p>
            *
      -     * The implementor must ensure that {@code sgn(compare(x, y)) ==
      -     * -sgn(compare(y, x))} for all {@code x} and {@code y}.  (This
      +     * The implementor must ensure that {@link Integer#signum signum}{@code (compare(x, y)) ==
      +     * -signum(compare(y, x))} for all {@code x} and {@code y}.  (This
            * implies that {@code compare(x, y)} must throw an exception if and only
            * if {@code compare(y, x)} throws an exception.)<p>
            *
      @@ -122,21 +127,16 @@
            * {@code compare(x, z)>0}.<p>
            *
            * Finally, the implementor must ensure that {@code compare(x, y)==0}
      -     * implies that {@code sgn(compare(x, z))==sgn(compare(y, z))} for all
      +     * implies that {@code signum(compare(x, z))==signum(compare(y, z))} for all
            * {@code z}.<p>
            *
      +     * @apiNote
            * It is generally the case, but <i>not</i> strictly required that
            * {@code (compare(x, y)==0) == (x.equals(y))}.  Generally speaking,
            * any comparator that violates this condition should clearly indicate
            * this fact.  The recommended language is "Note: this comparator
            * imposes orderings that are inconsistent with equals."<p>
            *
      -     * In the foregoing description, the notation
      -     * {@code sgn(}<i>expression</i>{@code )} designates the mathematical
      -     * <i>signum</i> function, which is defined to return one of {@code -1},
      -     * {@code 0}, or {@code 1} according to whether the value of
      -     * <i>expression</i> is negative, zero, or positive, respectively.
      -     *
            * @param o1 the first object to be compared.
            * @param o2 the second object to be compared.
            * @return a negative integer, zero, or a positive integer as the
      @@ -155,8 +155,8 @@
            * {@link Object#equals(Object)}.  Additionally, this method can return
            * {@code true} <i>only</i> if the specified object is also a comparator
            * and it imposes the same ordering as this comparator.  Thus,
      -     * {@code comp1.equals(comp2)} implies that {@code sgn(comp1.compare(o1,
      -     * o2))==sgn(comp2.compare(o1, o2))} for every object reference
      +     * {@code comp1.equals(comp2)} implies that {@link Integer#signum signum}{@code (comp1.compare(o1,
      +     * o2))==signum(comp2.compare(o1, o2))} for every object reference
            * {@code o1} and {@code o2}.<p>
            *
            * Note that it is <i>always</i> safe <i>not</i> to override

        Attachments

          Issue Links

            Activity

              People

              Assignee:
              darcy Joe Darcy
              Reporter:
              darcy Joe Darcy
              Reviewed By:
              Brian Burkhalter, Daniel Fuchs, Roger Riggs
              Votes:
              0 Vote for this issue
              Watchers:
              1 Start watching this issue

                Dates

                Created:
                Updated:
                Resolved: