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, <davidalayac...@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 >