Thoughts on a new method for equality on java.util.Objects?

David Alayachew davidalayachew at gmail.com
Sun Feb 11 10:13:20 UTC 2024


Since I am abandoning this idea, could someone close my JBS ticket?

And please link this message in the core-libs-dev archives as the close
reason -- I fear the potential for misuse, as mentioned in previous emails
on this thread.

https://bugs.openjdk.org/browse/JDK-8324718


On Sun, Feb 11, 2024 at 5:10 AM David Alayachew <davidalayachew at gmail.com>
wrote:

> Hello,
>
> Thank you for your response!
>
> So, I had thought of that before posting this email, and mentally, I had
> hand-waved away the concern by saying that there were plenty of situations
> where equality would still be useful without hashing. And while I still
> very much feel the same, the way you phrased your question is making me
> realize that my proposal could double nicely as a foot gun.
>
> Like you said -- Java's concept of equality is often attached to the
> concept of hashing. And while the 2 are permitted to exist out of alignment
> with each other, handling the asymmetric relationship is a dangerous
> balancing act that is error-prone.
>
> I fear that this method would be handing developer a foot gun. There
> already exists a large number of bugs caused by misalignment between equals
> and hashCode, and I see how this method would contribute to exactly that
> problem. Understanding that, I myself don't feel comfortable pushing this
> method forward.
>
> That said, I do appreciate you , @Philip Race <philip.race at oracle.com> , @Alan
> Bateman <Alan.Bateman at oracle.com> , and @- <liangchenblue at gmail.com> for
> being helpful as I go through the process. If nothing else, this was
> educational, and I now know exactly what concerns my next proposal should
> keep in mind when I try again.
>
> And I see the value of "socializing", as @Alan Bateman
> <Alan.Bateman at oracle.com> put it. I had thought this idea through plenty,
> but I can only see from my perspective.
>
> Thank you all for your time and help!
> David Alayachew
>
> On Sun, Feb 11, 2024 at 3:26 AM Holo The Sage Wolf <holo3146 at gmail.com>
> wrote:
>
>> How beneficial is it to extend equality method without appropriate
>> hashing?
>>
>> Specifically, given you are working in a domain specific world, e.g.
>> projection of Point3D into XY with equality semantics of equalsByXY, Java
>> does not know how to treat semantically equal objects as equal:
>>
>> var foo = new Point3D(0,0,0);
>> var bar = new Point3D(0,0,1);
>> var set = new HashSet<>(Arrays.asList(foo, bar));
>> assert set.size() == 1 \\ assertion failure :(
>>
>> The idea is fine, but you need to wrap a lot of Java's Standard Library
>> to respect the new semantics.
>>
>> I think that the idea can work nicely as a library, but not inside java.*
>>
>>
>> On Sun, 11 Feb 2024, 07:41 David Alayachew, <davidalayachew at gmail.com>
>> wrote:
>>
>>> Hello Core Libs Dev Team,
>>>
>>> I jumped the gun a bit and made a PR for this first. Alan Bateman kindly
>>> informed me of my faux pas, and now I'm trying to redo things correctly.
>>>
>>> I am looking to add a new method to java.util.Objects to facilitate
>>> equality checks, something that I hope fits well in place with the other
>>> methods in this class.
>>>
>>> Here is my basic idea. (Special thanks to @liach for guidance in
>>> creating this!)
>>>
>>> ```java
>>> import java.util.*;
>>> import java.util.function.*;
>>>
>>> import static java.util.Objects.*;
>>>
>>> public class Objects2
>>> {
>>>
>>>    public static <T> BiPredicate<T, T> equalsBy(final List<Function<T,
>>> ?>> functions)
>>>    {
>>>
>>>       requireNonNull(functions, "Objects.equalsBy cannot execute because
>>> the parameter is null!");
>>>
>>>       return
>>>          (final T o1, final T o2) ->
>>>          {
>>>
>>>             requireNonNull(o1, "Cannot check for equality because the
>>> first object is null!");
>>>             requireNonNull(o2, "Cannot check for equality because the
>>> second object is null!");
>>>
>>>             for (final var function : functions)
>>>             {
>>>
>>>                requireNonNull(function, "Cannot check for equality
>>> because the function is null!");
>>>
>>>                final var r1 = function.apply(o1);
>>>                final var r2 = function.apply(o2);
>>>
>>>                final boolean theyAreEqual = Objects.equals(r1, r2);
>>>
>>>                if (!theyAreEqual)
>>>                {
>>>
>>>                   return false;
>>>
>>>                }
>>>
>>>             }
>>>
>>>             return true;
>>>
>>>          }
>>>          ;
>>>
>>>    }
>>>
>>>    public static void main(final String[] args)
>>>    {
>>>
>>>       record Point3D(int x, String y, int z) {}
>>>
>>>       final Point3D a = new Point3D(1, "2", 3);
>>>       final Point3D b = new Point3D(1, "2", 4);
>>>
>>>       final BiPredicate<Point3D,Point3D> equalsByXY  =
>>> equalsBy(List.of(Point3D::x, Point3D::y));
>>>       final BiPredicate<Point3D,Point3D> equalsByXYZ =
>>> equalsBy(List.of(Point3D::x, Point3D::y, Point3D::z));
>>>
>>>       System.out.println(equalsByXY.test(a, b));  //true
>>>       System.out.println(equalsByXYZ.test(a, b)); //false
>>>
>>>    }
>>>
>>> }
>>> ```
>>>
>>> The concept is simple -- I want to make it easy to create ad-hoc equals
>>> methods.
>>>
>>> Object equality is domain-specific -- in some domains, 2 objects are
>>> equal, but in another, they are not. The object's equals method is not
>>> always a good spot to put this logic into, largely because we don't always
>>> know what domain we are in. The object's equals method is where a good
>>> default should be placed, not logic for every domain. And even if we tried,
>>> it's difficult, if not impossible, to apply equality for the correct domain
>>> if both objects are of the same type.
>>>
>>> So, for domain-specific contexts, I would like to introduce this method.
>>> This method (which should be constant-foldable, thanks again for the help
>>> @liach!) lets you clearly say that you are taking 2 objects and comparing
>>> them by the following methods that apply to both. And due to the nature of
>>> lambdas, developers are not constrained to just the getters of the object
>>> in question -- any function that takes in T is fair game. This allows
>>> flexibility, and lets developers keep their objects simple, thus
>>> facilitating things like DOP.
>>>
>>> Now, perhaps this makes more sense on the BiPredicate interface instead.
>>> However, since this was more equality focused, I figured Objects was a
>>> better fit.
>>>
>>> Any thoughts?
>>>
>>> Thank you all for your time and help!
>>> David Alayachew
>>>
>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/core-libs-dev/attachments/20240211/cc849d7f/attachment.htm>


More information about the core-libs-dev mailing list