I am trying to override the equals method in an abstract class. Is there a way that I can check if the parameter Object o is an instance of the class that has inherited the method and to then typecast o to same type as the class that has inherited the equals method?
This is what I have tried so far, but I am receiving the error variable of type java.lang.Object. Is there a reason that o is not being typecast? The getOperand()
method returns a compound type.
#Override
public boolean equals(Object o){
if (o.getClass() == this.getClass()){
this.getClass().cast(o);
return (o.getOperand().equals(this.getOperand()));
}
else {
return false;
}
}
You need to :
Assign the result of the cast to your class reference type so that the compiler is able to find the getOperand method since Object class doesn't have such a method.
Use the equals method to compare the operand values rather than using ==.
Use instanceof so that the solution supports mixed type equality. (Read Liskov Substitution Principle. Optional if you don't want to support mixed type equality)
Mark the equals method as final to prevent subclasses from overriding it inorder to support symmetry (if x.equals(y) then y.equals(x). Not required if not supporting mixed type equality)
public abstract class EqualsTest {
public final boolean equals(Object o) {//make equals final to maintain symmetry
if (o instanceof EqualsTest) {//use instanceof
EqualsTest e = (EqualsTest)o;//cast and assign to EqualTest reference
return e.getOperand().equals(this.getOperand());//use equals here
} else
return false;
}
}
}
With the above changes, sub-classes can inherit the equals method as is :
class EqualsTestSubClass extends EqualsTest {
//inherts the equals and getOperand methods.
}
class EqualsTestSubClass2 extends EqualsTestSubClass {
//inherts the equals and getOperand methods.
}
You can then check equality of an EqualsTestSubClass instance which is a direct subclass of EqualsTest and EqualsTestSubClass2 instance which is a subclass of EqualsTestSubClass instances as follows :
EqualsTest e1 = new EqualsTestSubClass();
EqualsTest e2 = new EqualsTestSubClass2();
System.out.println(e1.equals(e2));
Note : It is a better idea to directly compare the operand field in the equals method rather than using getOperand since getOperand is overridable.
Related
I am trying to sync users between two different locations, therefore I keep existing users in a list, and hence do a comparison at a set time interval to see if the user should be added (new) or just updated.
I have a class User that is the subclass to Principal.
However my compare on the list does not work; I googled a bit and found that you have to override the equals method, and I do - but that code does not seem to be executed, it goes into ArrayList.class (primitive) and executes the contains method there.
Is this because my class already extends the superclass Principal?
What are my options if I want to execute the equals that I defined in User class?
public class User extends Principal
{
// some protected properties
...
#Override
public boolean equals(Object obj) {
return (this.getAlias().equals(((User) obj).getAlias())
&& this.getEmailAddress().equals(((User) obj).getEmailAddress()) && this.getCellNumber().equals(((User) obj).getCellNumber()));
}
}
The Principal class does not override the equals method, and more importantly, the properties I check for equality, is only contained in the subclass - User. Therefore it makes sense to check it here.
So in short, I have an ArrayList of Users, and I would like to check whether a certain User already exists or not. I call compare on the list, but it always fails, indicative that the method equals is not overrided properly in my code.
Any suggestions?
You should not implement equals() (and hashcode()) in a super class.
The reason is that when equals() returns true hashcode() must return same value
Imagine you have class Point2D and class Point3D extending the other.
Shall a point2D be equal to a point3D with same area coordinates?
If so then point3D must return the same hashcode as the "equal" point2D and that means that you cannot not store more that one poin3d with same area coordinates in a Hash bases collection (eg.: as keys in a HashMap).
Overriding equals is not as evident as it looks
equals with null must return false
equals with an object of a different class must return false because of symetry a.equals(b) <=> b.equals(a)
java
#Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj == null || obj.getClass()!=getClass()) {
return false;
}
return Object.equals(this.getAlias(),((User) obj).getAlias())
&& Object.equals(this.getEmailAddress(),((User) obj).getEmailAddress())
&& Object.equals(this.getCellNumber(),((User) obj).getCellNumber()));
}
Also if object is used in hash collections it must override hashCode so that two objects that are equals must return the same hashCode, the contrary is not true.
The problem probably comes from you instantiating a List<Person>. The compiler can't know if every subclasses of Person override equals. To correct this, you should promise your compiler you'll override this method, which you can do by changing your Person class to an abstract class.
public abstract class Person {
#Override
public abstract boolean equals(Object o);
}
public class User extends Person {
// Some stuff...
#Override
public boolean equals(Object o) {
if (o == null || ! (o instanceof User))
return false;
// etc
}
}
According to the book Effective Java.If you have override the equals method,then you must override the hashcode method.
some advice when you override the equals method:
1. equals with null return false.
2. !(obj instanceof this) return false.
3. cast obj to this class and compare the parameters in the obj and this class.
return the result in the end
You should use the contains methode of the arrayList
https://docs.oracle.com/javase/7/docs/api/java/util/ArrayList.html
In my GameObject class I have the following method to check if the GameObject would be colliding with another object if it moved to the specified position:
public boolean collisionAt(Vector2d position, Class<? extends GameObject>... exclusions) {
if (getBounds() == null)
return false;
Rectangle newBounds = getBounds().clone();
newBounds.setPosition(position);
// Check collisions
for (GameObject object : new ArrayList<>(gameObjects)) {
if (object.getBounds() != null && newBounds.intersects(object.getBounds()) && object != this) {
boolean b = true;
for (Class<? extends GameObject> exclusion : exclusions) {
if (object.getClass().isInstance(exclusion))
b = false;
}
if (b)
return true;
}
}
return false;
}
I want to allow the program to define exclusions, for example if I don't want this method to return true if it collides with a Spell. But for some reason the Class.isInstance() line always returns false. I even tried this:
System.out.println(Spell.class.isInstance(Spell.class));
and the console outputs false! What's going on here?
The isInstance tests if the given object is an instance of the Class, not if the given Class is a subclass of the Class.
You have your invocation backwards. You need to test if the gameObject is an instance of one of the exclusion classes.
if (exclusion.isInstance(gameObject))
From official Javadocs
public boolean isInstance(Object obj)
Determines if the specified Object is assignment-compatible with the object represented by this Class. This method is the dynamic equivalent of the Java language instanceof operator. The method returns true if the specified Object argument is non-null and can be cast to the reference type represented by this Class object without raising a ClassCastException. It returns false otherwise.
You need to pass in the object of class rather than the class itself.
Example
SomeClass object = new SomeClass();
System.out.println(SomeClass.class.isInstance(object));
You need to pass in an instance of the class in question rather than a class literal. E.g.
Spell spell = new Spell();
System.out.println(Spell.class.isInstance(spell));
isInstance determines if the specified Object is assignment-compatible with the object represented by this Class. You're passing it a class when it expects an object.
The opposite should work:
Spell.class.isInstance(spell)
When we override the equals() method in Java, I know that Object needs to be a parameter, but I wonder - why Object?.
Second, let us say we override hashcode() and implement equals(), but set the parameter in equals() to MyClass instead of Object (MyClass being the class whose equals() method we override). Will we still get the expected behavior if we use HashMap?
Update: Yes, it will be overloading instead of overriding. But what will happen if we use HashMap with overloaded equals()? Also, I don't find the answer in related posts. Or is it something obvious that I am missing?
If you write an equals() method whose parameter is not Object, you are overloading the method, not overriding it.
Now, as for HashMap - HashMap calls equals to compare keys. The type of the compared keys is Object. Therefore, if you define an equals() method with a parameter whose not Object, this method will be ignored by HashMap.
I tried the following code :
public class SomeClass
{
int privateMember;
// note it's important to override hashCode, since if the hashCode of two
// keys is not the same, equals() won't be called at all
public int hashCode ()
{
return privateMember;
}
public boolean equals (Object other)
{
if (other instanceof SomeClass) {
return this.privateMember==((SomeClass)other).privateMember;
}
else {
return false;
}
}
public static void main(String[] args)
{
HashMap<SomeClass,String> map = new HashMap<SomeClass,String>();
SomeClass s1 = new SomeClass ();
SomeClass s2 = new SomeClass ();
s1.priv=4;
s2.priv=4;
map.put (s1, "something");
if (map.containsKey (s2)) {
System.out.println ("found!");
} else {
System.out.println ("not found!");
}
}
}
This code outputs "found!".
Now, if you run the exact same code, but replace the equals method with :
public boolean equals (SomeClass other)
{
if (other instanceof SomeClass) {
return this.privateMember==((SomeClass)other).privateMember;
}
else {
return false;
}
}
The output will be "not found!", which means our equals method was ignored.
The collections use the equals and hashcode methods from the Object base class. Therefore you must override them in order for your custom class to provide an implementation. You can overload equals if you wish, and that would work for situations where some code knows that it's dealing with an instance of MyClass. However, this would be misleading.
All the collections classes are designed to work with instances of Object and Object provides a general purpose equals method.
You shouldn't really need to write an equals method directly. You can either generate one using your IDE, or use EqualsBuilder from Apache Commons (https://commons.apache.org/proper/commons-lang/javadocs/api-3.1/org/apache/commons/lang3/builder/EqualsBuilder.html) to help put it all together.
I’ve got a List<MyObject> list
And, I want to make use out of the list.contains() function.
It doesn’t seem to work at the moment, i.e. there are objects in the list that match, but is not being picked up by the comparator operator.
I’m guessing I need to write my own comparison operator in MyObject. What is the way to go about this?
You need to override public boolean equals(Object o) because contains uses it:
boolean contains(Object o)
Returns true if this collection contains the specified element. More formally, returns true if and only if this collection contains at least one element e such that (o==null ? e==null : o.equals(e)).
See How to implement hashCode and equals method for a discussion of simple and correct ways to override equals.
You need to be especially careful to override equals(Object o) and not just implement equals(MyObject o).
You only have to implement your own version of equals and hashcode on MyObject class.
The default equals will not check the attribute you define in a class. That's why you get the wrong result.
Your class needs to implement equals(). It's also useful to implement the Comparable interface, if you ever want to sort your objects E.g.
class MyObject implements Comparable<MyObject> {
public int compareTo(MyObject o) {
// do the compare!
}
public boolean equals(Object o) {
// check equality
}
}
Notice the documentation for List's contains method:
List.contains()
It states that it used the equals method to determine equality and therefore determine if the element exists in the list.
Also, note that when you overload equals you must overload hashCode.
You have to override equals() in MyObject.
public class MyObject
{
public boolean equals(Object obj)
{
if(this == obj)
return true;
if((obj == null) || (obj.getClass() != this.getClass()))
return false;
// object must be MyObject at this point
MyObject test = (MyObject) obj;
// Compare 'this' MyObject to 'test'
}
public int hashCode()
{
// generate your hash
}
}
Let us say we have a method which accepts two arguments o1 and o2 of type Object and returns a boolean value. I want this method to return true only when the arguments are instances of the same class, e.g.:
foo(new Integer(4),new Integer(5));
Should return true, however:
foo(new SomeClass(), new SubtypeSomeClass());
should return false and also:
foo(new Integer(3),"zoo");
should return false.
I believe one way is to compare the fully qualified class names:
public boolean foo(Object o1, Object o2){
Class<? extends Object> c1 = o1.getClass();
Class<? extends Object> c2 = o2.getClass();
if(c1.getName().equals(c2.getName()){ return true;}
return false;
}
An alternative conditional statement would be :
if (c1.isAssignableFrom(c2) && c2.isAssignableFrom(c1)){ return true; }
The latter alternative is rather slow. Are there other alternatives to this problem?
Don't bother with the fully qualified class names - just compare class references:
public boolean foo(Object o1, Object o2) {
return o1.getClass() == o2.getClass();
}
Classes are essentially unique by name and classloader - so this will return false if the objects are of the same class name but loaded by different classloaders, but that's probably appropriate: they could be completely different classes in all but name! If the classes have the same name and classloader, however, they'll have the same reference.
Note that this will throw a NullPointerException if either o1 or o2 is null, but again that's probably what you want.
Can you qualify rather slow? Compared to what? Also please elaborate your usage scenario? In java the Object api includes a equals/hashCode method paring that should be used to identify equality between objects. It appears as if this is what you may be attempting to find, ie:
new Integer(4).equals(new Integer(5))
In this case, the best implementation for overriding equals in SomeClass is as follows:
public boolean equals(Object obj) {
if (obj instanceof SomeClass) {
return ((SomeClass) obj).identityField.equals(identityField);
}
return false;
}
public int hashCode() {
return 31 * identity.hashCode();
}
Making sure you provide an identity within your object.