Custom equals outside of the class definition - java

Is there a way to write a custom equals method compactly when trying to compare two objects but not relying on those objects' internal equals() method? For example, if I had two Foo objects like so:
public class Foo {
int id;
String name;
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Foo item = (Foo) o;
return id == item.id && listId == item.name;
}
}
But, in a use case for the foo objects, lets say I just want them equated by their id. Keep in mind this is a toy example and the real use case has many more fields so I may have an object with 6 fields all being used in the overridden equals method but may want to only use 3 of them to do an equals outside the class when comparing two objects.
List<Foo> objType1;
List<Foo> objType2;
Compare the two lists and assert each Foo object is equal but only use a subset of the fields of Foo in the comparison. I dont want to touch the actual Foo object in any way. How can i do this outside of asserting by handing that each field I am interested in is equal?

The complexity of the solution really depends on the use case. Universally - no, one cannot do it without direct bytecode manipulation.
A lot of Java APIs allow using custom Comparator as an option. E.g. if you want to compare two lists of Foo's with custom comparator:
List<Foo> list1 = ...
List<Foo> list2 = ...
Comparator<Foo> c = Comparator.comparing(Foo::getId);
boolean equal = list1.size() == list2.size() &&
IntStream.range(0, list1.size())
.allMatch(i -> c.compare(list1.get(i), list2.get(i)) == 0);
Note, this solution does not check for list1 or list2 being null's and assumes your Foo has standard getters like getId(). Also, if you don't deal with lists but with abstract iterable collections, you might want to look into zip implementations. Comparator.comapring() is chainable like this Comparator.comparing(Foo::getId).thenComparing(Foo::getAttrX).thenComparing(Foo:getAttrY)...; which is fairly convenient and readable.
Another option you might want to look at to customize equals() for a variety of cases is to use Proxy.newProxyInstance() with your custom equals override, i.e. auto-create proxy wrapper around your instances when filling collections etc.
UPDATE
Using Comparator.comparing().thenComparing()... chain might seem to be tricky. It helps to understand that lambdas for these functions need to extract either primitives or Comparable descendants (i.e. implements Comparable and has compareTo() method) from the given top level object reference - in our case, Foo. If Foo would have a Bar getBar() accessor that has to be included into comparison, then either go down to the primitive - .thenComparing(f -> f.getBar().getName()) or make Bar implement Comparable. Don't forget to treat nulls properly, if you go the route of custom lambda functions - which is, sometimes, a challenge on it's own.
The positive of an approach in this answer is that Comparator defines a total order over the set of objects stored in the lists. The negative of this approach is, this total order is not really needed for simple comparison - if it is really all you need. In some cases, writing a good old for loop and doing all the comparisons "manually" might be less confusing. From experience, in most cases having an order is beneficial, if not now, then in the next release.

You could add some extra field(s) to your Foo class that will be used in your overriden equals method to specify which fields should be used to deteremine whether two instances are equal. You could then set those fields before comparing. Then your equals method might contain:
if ( useFiledA ) {
if ( this.a != item.a ) return false;
}
if ( useFiledB ) {
if ( this.b != item.b ) return false;
}
// etc.
return true;

implements Comparable from your classes. then write the compareTo method. iterate ocer the first list and call colpareTo over all the elements of the second list

Related

How to use an object that was called from a method in Java?

I'm trying to figure out what the syntax is for calling an object inside a method..
Pseudocode:
boolean check(Object someObject) {
return someObject == theOtherObject;
}
public static void main(String args[]) {
someClass one = new someClass();
someClass two = new someClass();
one.check(two);
}
So the check method is supposed to check whether the two objects are equal, but how would I specify the other object (theOtherObject should be one)?
Thanks in advance!
One word answer: this
boolean check(Object someObject) {
return someObject == this;
}
which will test object identity only. You should override equals and use that.
if (one.equals(two)) {
// ...
}
You can have the boolean check(Object o) method inside SomeClass and check
boolean check(Object o) {
this == (SomeClass) o;
}
This would work only if both reference variables are pointing to same object. Moreover the right way to check if two objects are meaningfully equal would be to use the inherited equals and hashCode method.
Override equals and hashCode method.
Why do I need to override the equals and hashCode methods in Java?
https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#equals-java.lang.Object-
So what you're asking for there is actually already a command in java.lang.Objects class to compare to objects.
one.equals(two)
the comparison this does is called a shallow comparison. So if that's what you're looking to do then this would work. For reference, the definitions of shallow comparison defined by geeksforgeeks.org is
Shallow comparison: The default implementation of equals method is defined in Java.lang.Object class which simply checks if two Object references (say x and y) refer to the same Object. i.e. It checks if x == y. Since Object class has no data members that define its state, it is also known as a shallow comparison.
if you're looking to do a more complicated comparison you're the best bet would be to actually override the equals command in the one class file
this article would be a good place to start to learn more about this topic.
https://www.geeksforgeeks.org/equals-hashcode-methods-java/

Run equal method defined by parent class from outside the class

If I have a complex hierachy of inheritance and I want to do an equality check that depends on a specific equality is there a way to ensure I run using that equality check and not a version overridden by a child class that may behave different then I want?
To give an example lets say I have a foo with it's own equality class, and then I have something like boo with an equals class sort of like below sudocode (too lazy to right it out in full)
class Foo {
int id;
int otherId;
int count;
public boolean equals(Object other){
//check other instance of foo and re-cast
return (id==other.id && otherId==other.otherId)
}
}
class Bar extends Foo{
private someObject uniqueValue;
public boolean equals(Object other){
//check other instance of foo and re-cast
return super.equals(other) && other.getUniqueValue.equals(this.getUniqueValue);
{
}
Then I have some method which takes too foo objects which reference the same foo and add up the counts for them
public combineFoos(Foo foo1, Foo foo2){
if(! foo1.equals(foo2))
throw IllegalArgumentException("no match");
Foo combinedFoo=new Foo(foo1.id, foo1.otherId, (foo1.count + foo2.count))
return combinedFoo
}
}
In theory this all works, until the bar value comes in. Now if I call combineFoo and pass in a bar to foo1 it fails, because bars equal method checks foo2 is an instanceOf bar. I really don't care if foo2 is a bar, the information I need is available for any foo. For that matter if I pass in two unequal bars that both are equal as defined by foo's equality method (ie only the uniqueValue fields are different) I would want the combineFoo to still accept them as equal Foo's, even if not equal by Bar's definition.
The problem is that I want to do an equality check on exactly what a FOO considers as equal, but I don't know what bizarre inherited class I may actually receive. Is there a way to work around this, to basically say in my combineFoo to always use a FOOs definition of equals, even if it is overridden by a child class?
Related, would this be a far worse idea then I imagine it be even if doable, or is there some other way to handle something like the above example, short of effectively re-writing foo's equal method within the combineFoo?
Your equals methods fail the symmetry test, if a.equals(b) then b.equals(a). The obvious way to fix it would be to check the class like so:
public boolean equals(Object other){
if(other.getClass() != this.getClass()) return false;
return (id==other.id && otherId==other.otherId)
}
Having said that you may wish to allow some pairs of subclasses to be considered equal to each other - in which case I suggest you check out this question:
Java - equals method in base class and in subclasses

Implementing bidirectional relations between objects of the same class

I have to implement a class whose instances have a bidirectional relation to each other. For example I have the class FooBar which should offer the method sameAs(FooBar x) and maintain a Set for each instances containing its equivalent instances. So if I call foo.sameAs(bar), the Set in foo should contain bar and vice versa. Invoking bar.sameAs(foo) doesn't work, of course.
For clarifiction: the instances of this class are only semantically equal. equals should still return false.
The solutions I've come up with is either to implement a private method internalSameAs(FooBar x) which is invoked from sameAs(FooBar x) or to use a static method sameAs(FooBar x, FooBar y).
Solution 1:
class FooBar {
Set<FooBar> sameAs = new HashSet<FooBar>();
public void sameAs(FooBar x) {
this.internalSameAs(x);
x.internalSameAs(this);
}
public void internalSameAs(FooBar x) {
sameAs.add(x);
}
}
Solution 2:
class FooBar {
Set<FooBar> sameAs = new HashSet<FooBar>();
public static void sameAs(FooBar x, FooBar y) {
x.sameAs.add(y);
y.sameAs.add(x);
}
}
Which one would you prefer and why? Or is there another way I didn't think about?
The naming you've used is confusing. sameAs sounds as though it's a test which should return a boolean, but from your code it seems it would be more appropriately named declareSameAs. When you call foo.sameAs(bar), you're declaring that foo and bar are the same, not doing a test, correct?
The problem is that with your code you can declare
x.sameAs(y);
y.sameAs(z);
but it won't be the case that x is the same as z, which is presumably not what you want (and if it is what you want, you definitely need to change the method name).
It seems to me you want to divide your instances into sets, and have each instance keep a reference to the set it's in (not to a separate set internal to the instance). When you make a new declaration that two instances are the same, you need to combine the sets, and ensure all affected instances have a reference to the combined set.
are you flexible with the data structures to be used? If so you could use a Multimap (from Guava Collections) that is static amongst all the instances of the class FooBar. In that Multimap you can have the keys as FooBar references (or a unique id if you have one) and the values would be the references (or id.s) of the FooBars that have the sameAs relation.
Maybe there's a different way: sameAs sounds pretty similiar to equals. If we do not need equals for something else, then I'd simply implement the equals method on FooBar so that we simply do a
if (foo.equals(bar))
System.out.println("We're equal (aka: 'equivalent/the same')");
In this case, we do not need any set - just a rule to determine, if two instances are equal.
You could store the sameness information in a separate datastructure outside of those classes. A central map could do the job:
HashMap<FooBar, Set<FooBar>> sameFooBars;
If you have "same" objects, simply add them to the map:
public static void addSameObjects(FooBar foo1, FooBar foo2) {
Set<FooBar> set = getMap().get(foo1);
if (set == null) {
set = new HashSet<FooBar>();
getMap().put(foo1, set);
}
set.add(foo2);
// serious implementation avoid code duplication...
set = getMap().get(foo2);
if (set == null) {
set = new HashSet<FooBar>();
getMap().put(foo2, set);
}
set.add(foo1);
}
And the test:
public static boolean isSame(FooBar foo1, FooBar foo2) {
if (getMap().get(foo1) == null)
return false;
return getMap().get(foo1).contains(foo2);
}
Do you really need to maintain a list of equivalences in ALL objects? If possible I would separate the set of equivalences from the objects themselves. This will be easier to maintain.
Then you can use the multimap of #posdef or more simply a Map> to stay with standard JAVA API.
Your "bidirectional" samesAs(...) method sounds like Object.equals(...), which, according to javadoc is a "an equivalence relation on non-null object references". If this is what you want, then you just have to override equals in your class.
I'm a bit lost when you say that "FooBar shouldmaintain a Set for each instances containing its equivalent instances". If you want to build equivalent classes for FooBar objects, then I think it's a good idea to use a java Collection to represent them, and more precisely a Set.
Here is a quickly hacked example:
public class FooBar {
#Override
public boolean equals(Object other) {
// do whatever fancy computation to determine if
// the object other is equal to this object
}
}
and for the equivalent class:
#SuppressWarnings("serial")
public class FooBarEquivalentClass extends HashSet<FooBar> {
#Override
public boolean add(FooBar e) {
if (isEmpty())
return super.add(e);
else if (e.equals(iterator().next()))
return super.add(e);
else
return false;
}
}
"same as" but not "equal to" sounds like you should be using Comparable.
I think it makes more sense to implement compareTo() or sameAs() as an instance method rather than a static since you will always need two real instances to do any comparison.
Sounds like what you want are to separate the equivalence groups from the object instances.
Make a Map<FooBar, Set<FooBar>> and note that when you lookup an object the set will include itself.

How to compare Classes and Inherited Classes in Java

I have two classes - Task (which implements Comparable) and DeadlinedTask (where DeadlinedTask extends Task). And for each of them I have written an overloaded compareTo function (each has compareTo(Task) and compareTo(DeadlinedTask)).
The idea is that I can sort normal Tasks by category, and DeadlinedTasks by deadline, but I also want all of the DeadlinedTasks to be sorted above the Tasks.
When I call Collections.sort(myListOfTasks) on a list of only Tasks (no DeadlinedTasks), everything works like a charm.
However when I have a list of both Tasks and DeadlinedTasks, the objects change order, but they are not fully sorted.
I have tried returning numbers other than 1 on the interclass compares (1, 1000, 1000000 all did the same thing). Is there any way to do this through compareTo and Collections.sort, is there a different java functionality I can use, or do I have to write my own search function (as a Comparator?)?
Task compareTo Methods:
public int compareTo(Task other){
if(this.GetCategory().compareTo(other.GetCategory())==0)
return this.GetName().compareTo(other.GetName());
else
return this.GetCategory().compareTo(other.GetCategory());
}
public int compareTo(DeadlinedTask other){
return 1;
}
DeadlinedTask compareTo Methods:
public int compareTo(Task other){
return -1;
}
public int compareTo(DeadlinedTask other){
if(this.GetDeadline().compareTo(other.GetDeadline())==0)
return this.GetName().compareTo(other.GetName());
else
return this.GetDeadline().compareTo(other.GetDeadline());
}
Thanks for any help
... or do I have to write my own search function (as a Comparator?)?
Yes. I think that's the best way.
The normal way to handle equals and compareTo is to return false (for equals) or throw ClassCastException (for compareTo) if the arguments actual type doesn't match the actual type of this.
If you try to implement equals or compareTo for subtypes, you can easily create semantic anomalies such as:
a.equals(b) and b.equals(a) returning different values, or
a.compareTo(b) and b.compareTo(a) returning inconsistent values.
Avoiding those anomalies would entail making the supertype aware of the subtype. That is a bad from a design perspective because it restricts your ability to create more subtypes in the future.
For use-cases where you need to implement a rule that orders instances of two or more different classes, a Comparator is the best solution.
Per class, only one compareTo method can be used to implement the Comparable interface. If you use Comparable without generics, then this is
public int compareTo(Object o)
If you're using generics, e.g. Comparable<Task>, then it's
public int compareTo(Task o)
Your compareTo(DeadlinedTask o) method will be ignored concerning the Comparable<Task> interface. It just "accidentally" has the same name, but it's an independent overloading.
(By the way, it's not possible to implement both Comparable<Task> and Comparable<DeadlineTask>).
So what you'll have to do instead, is change your Task.compareTo(Task o) method to use instanceof (it has to use runtime information after all). I agree with Stephen, that it would even be better to write a Comparator.
Comparable defines a natural order for all instances of a class. So if DeadlinedTask should always come before Tasks, then the compareTo method should implement it.
You should not redefine compareTo in DeadlinedTask, because this would break the contract of anti-commutativity : if (t1.compareTo(t2) > 0), then t2.compareTo(t1) < 0.
I would thus completely avoid to implement Comparable in the Task class, and use a dedicated comparator when sorting a collection of tasks. If you really want your task to implement Comparable, than you need to make its implementation depend on the existence of DeadlinedTask (which is not very OO) :
public class Task implements Comparable<Task> {
// ...
public final int compareTo(Task t) {
if (this instanceof DeadlinedTask) {
if (t instanceof DeadlinedTask) {
return ((DeadlinedTask) this).getDeadline().compareTo(((DeadlinedTask) t).getDeadline());
}
else {
return -1;
}
}
else if (t instanceof DeadlinedTask) {
return 1;
}
else {
return this.category.compareTo(t.category);
}
}
}
Note that Java uses a lower-case letter at the beginning of methods (getDeadline(), and not GetDeadline()), and that you don't need to use getters to access private properties of your own class.
In addition to what StevenC have said, if you know in advance that you will have a hierarchy of value objects, you can check whether the class of the argument of the compareTo() method is a subtype of the class of the object and if yes, reverse the comparison, so you will always have the child comparing against the parent:
public boolean compareTo(Object o) {
// check for null
boolean isSubtype = getClass().isAssignableFrom(o.getClass()) && getClass()!=o.getClass()
if (isSubtype) return -((/*cast to this type*/) o).compareTo(this);
}
This way, the comparison remains consistent and the base type does not to be aware pf each individual subtype, but just that subtypes exist.
Yes it seems a comparator is the simplest (& cleanest way)
But you can simply delegate te bulk ot the work to the compareTo(...) methods you have already written, all you really need to add is code to handle comparison between the sub and super classes:
public int Compare(Task t1, Task t2) {
if (t1 instance of DeadlinedTask && !(t2 instanceof DeadlinedTask))
return 1;
else if (t2 instance of DeadlinedTask && !(t1 instanceof DeadlinedTask))
return -1;
else
return t1.compareTo(t2);
}
but it just occured, how are you declaring the classes? do you include Comparable in the implements clause of the Task class and visa versa? if not, then perhaps when the lhs object is a Task, then only compare(Task) gets called ?? otherwise you need to have both in the implements clause ie:
class Task implements Comparable<Task>, Comparable<DeadlinedTask>
The magnitude of the value returned will not change anything, ie returning 1 and 1000000 is exactly the same, as tests are only < 0, > 0 and == 0 (this contract IS specified in the docs for the Comparator interface. I used to tell students trying to remember what return values mean, to imagine comparing ints, then we could just write:
int compare (int a, int b) { return a - b; }

Sort a Vector of custom objects

I'm trying to sort a Vector in java but my Vector is not a vector of int, it is a vector of objects
the object is:
public MyObject() {
numObj = 0;
price = new Price();
pax = new Pax();
}
so I have a Vector of MyObject and I want to order it by numObject, how do i do it, I'm new in java?
Thank you so much for all your help.
I assume you are using Collections.sort(..). You have two options:
make your class implement Comparable
when sorting, create a custom Comparator
An example for implementing Comparable would be:
public class MyObject implements Comparable<MyObject> {
// ..... other fields and methods
public int compareTo(MyObject other) {
return numObj - other.getNumObj();
}
}
This will mean the items are sorted in ascending order. If you want other, custom sorting, you can create a Comparator that defines the custom comparison and pass it as an argument to Collections.sort(..);
To sort a vector of object, first the class MyObject must implement Comparable and implement method compareTo(Object), then use Collections.sort(Vector)
class MyObject implements Comparable<MyObject> {
public int compareTo(MyObject a) {
//return either 1, 0, or -1
//that you compare between this object and object a
``}
}
//and in your logic write this line
Collections.sort(myVector);
check the JavaDoc of Vector
Implementing Comparable will work fine. However, it should be done with caution in order to prevent future bugs. The implementations above are great for sorting, but if you later decide to put your MyObject instances into a SortedSet (such as TreeSet), you will have an unexpected behavior. Items which are not identical will collide with each other if they have the same value of the numObject data member. This happens because sorted sets use the Comparable implementation (by default) in order to determine equality.
See http://eyalsch.wordpress.com/2009/11/23/comparators/ for details.

Categories

Resources