Hello,

I need a sponsor for following JDK change.

Summary
-------

Provide means for type safe comparison for equality by introducing
type-parameterized interface `java.lang.Equable<T>` with method `boolean
equ(T)`.
(The actual names of the interface and the method is subject to discussion.)


Goals
-----

The primary goal is to avoid programmer mistakes when values of different
and
incomparable types are compared using `equals` method, which always returns
`false`
in such cases according to its contract.


Non-Goals
---------

Custom hashers and equators for Collection Framework are out of the scope
of this proposal.


Motivation
----------

This is quite common approach to use `equals` method to compare value for
equality.
However, it may happen that the values have different incomparable types
and, therefore,
the result of such a comparison is doomed to `false`. For example,
instances of
`java.lang.Integer` and `java.lang.Long` will never be equal even though
they represent
the same numerical value. I.e. this code

    Long l = 42L;
    Integer i = 42;
    if (l.equals(i)) { // always false
        ...
    } else {
        ...
    }

is very likely a mistake because the else-branch will never be taken. The
same applies to attempts
to compare among themselves instances of `java.lang.Short`,
`java.lang.String`, `java.util.Date`,
`java.util.List`, `java.util.Set`, `java.util.Map` etc. The reason why such
erroneous comparisons
remain unnoticed is the fact that the `equals` method accepts any value.
One way to make mistakes
with such comparisons more visible it to use `compareTo` method.

    Long l = 42L;
    Integer i = 42;
    if (l.compareTo(i) == 0) { // compilation error
        ...
    } else {
        ...
    }

However this approach has some drawbacks:
* It's counter-intuitive and unnecessarily verbose. It's not clear at
glance why `compareTo`
  is used instead of `equals`.
* It can be inefficient because there are cases when an attempt to figure
out whether given value
  is less or greater that the other value requires more effort than an
attempt to figure out
  whether the values are just not equal. For example, when we compare
strings and have noticed
  that the strings have different length we can stop here and do not scan
them in order to
  figure out, which one is greater than the other. Another example is the
class that represents
  rational number as a reduced fraction. To make sure that the rational
numbers are equal, we can
  just compare their numerators and denominators. But in order to find out
which one is greater
  than the other we can't avoid arithmetic operations (multiplications).
* It's not applicable to the classes that do not implement
`java.lang.Comparable<T>`. For example,
  collections, complex numbers, points in 2D or 3D space etc.

Therefore it's proposed to introduce interface `Equable` in order to
explicitly mark comparable classes.

    public interface Equable<T> {
        boolean equ(T o);
    }

Let `Comparable` extend it and provide default method for `equ`.

    public interface Comparable<> extends Equable<T> {
        int compareTo(T o);

        default boolean equ(T o) {
            return compareTo(o) == 0;
        }
    }

Provide efficient implementation of `equ` method for some JDK classes, at
least those that are in
`java.lang` package. Also it would be desiable to let `java.util.List`,
`java.util.Set`,
`java.util.Map` and `java.util.Map.Entry` to extend `Equable` interface as
well (but without
extending `Comparable`).

===

I am sending a set of patches. I split the whole change into several
logical parts.

* equable-0.patch contain `java.lang.Equable`, `java.lang.Comparable`,
`java.util.Objects.equ`

* equable-1.patch contain `j.l.Boolean`, `j.l.Byte`, `j.l.Character`,
`j.l.Double`, `j.l.Enum`,
  `j.l.Float`, `j.l.Integer`, `j.l.Long`, `j.l.Short` and `j.l.String`

* equable-2.patch contain `j.u.Optional`, `j.u.OptionalDouble`,
`j.u.OptionalInt`,
  `j.u.OptionalLong`

* equable-3.patch contain `j.u.List`, `j.u.AbstractList`, `j.u.Set`,
`j.u.AbstractSet`, `j.u.Map`,
  `j.u.AbstractMap`

* equable-4.patch is a tricky one. It contains
`com.sun.tools.jdi.EventSetImpl`. Previously this class
  extended `java.lang.ArrayList` and implemented `java.lang.Set` through
`EventSet`. Therefore it appeared
  that it implemented both `java.util.List` and `java.util.Set` at the same
time. This is not right,
  because these interfaces have different contracts regarding equality. So
I had to change it by extending
  `java.lang.AbstractSet`.

Thank you,
Dmytro

Reply via email to