== vs. equals() references/pointers JAVA - java

I'm learning about == vs. equals() and doing various examples. For this one, I understand why r==s is false, but why is r.equals(s) false if they now both have the same content?
public class StringProgram{
public static void main(String[] args) {
Person r = new Person("A");
Person s = new Person("J");
s.setName("A");
System.out.println(r.getName());//A
System.out.println(s.getName());//A
System.out.println(r==s);//false
System.out.println(r.equals(s));//false
}
}
Here is the code of Person :
public class Person{
private String name;
public Person(String d){
name=d;
}
public void setName(String a){
name=a;
}
public String getName(){
return name;
}
}
EDIT: I see that I have to override it now, but I still don't understand how the assignment works in the example below. I just don't understand why t.getName() is Keen if t is assigned to u.
public class StringProgram{
public static void main(String[] args) {
Person t = new Person("Gene");
Person u = t;
u.setName("Keen");
System.out.println(t.getName());//Keen
System.out.println(t.equals(u));//true
}
}

All classes in java inherit from the Object class. When you do an r.equals(s), it uses the .equals() method as defined in Object class. To make it work as you desire, you need to define the .equals() method for the Person class by overriding the .equals() method.
Eg. Add this in your Person class:
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
Note: I used eclipse to generate the equals and hashcode method for me. You may read this thread to understand why hashcode() needs to be overridden.

equals() is just a method, there's nothing special about it. In particular, it doesn't automatically know how to compare the content of two objects.
The equals() method of class Object does the same thing as ==. If you do not override the equals() method in your class Person, then it's not automatically going to compare the content of Person objects.
You have to override the equals() method in class Person so that it does the comparison in the way you want.
public class Person {
private String name;
// ...
#Override
public boolean equals(Object o) {
if (!(o instanceof Person)) {
return false;
}
return ((Person) o).name.equals(this.name);
}
}

Java can't guess what you want to do, you have to tell it that two persons are the same if they have the same name. You must override equals :
#Override
public boolean equals(Object o) {
if (o == this) return true;
if (o == null) return false;
if (!(o instanceof Person)) return false;
Person that = (Person) o;
if (name == null && that.name == null) return true;
if (name == null || that.name == null) return false;
return that.name.equals(name);
}

All java classes inherits from Object, and to properly compare your class you should override the equals() method.
public Person(String d){
name=d;
}
public void setName(String a){
name=a;
}
public String getName(){
return name;
}
#Override
public boolean equals(Object obj) {
return obj instanceof Person &&
this.getName().equals(((Person)obj).getName());
}

Related

Java - How to check if two objects, from the same abstract class, are equal

I'm writing a program which contains an abstract class of 'Book', and I have two classes ('LearnBook' and 'ReadingBook') which inherit from 'Book'.
Book:
Public abstract class Book {
protected String name;
protected String author;
LearningBook:
public class LearningBook extends Book {
private String subject;
ReadingBook:
public class ReadingBook extends Book {
private int numberOfPages;
At the main class I have Book array which can include any instance of Book.
I want to add a method which checks if two Book objects are exactly the same, to prevent duplicating in the Book array. it looks like this:
public boolean sameBookCheck(Book book1, Book book2)
So my first idea was to write an isEqual() method in the Book class, which checks if the "name" and the "author" are equals.
But then I need to check if it's a learning book or reading book so I could know if I need to compare the "subject" value or the "numberOfPage" value.
I have no idea how to do it and I'd appreciate your help.
You can use the following design:
In Book abstract class have an equals() function and check whether the other object is of type Book and have same values in all fields.
In LearningBook and ReadingBook have equals() function which first checks whether the other object is of same class, then call Book's equals() function, checking the fields of abstract class, and then check whether field(s) of current class have same values or not.
Have a look at the code:
abstract class Book {
protected String name;
protected String author;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((author == null) ? 0 : author.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!(obj instanceof Book))
return false;
Book other = (Book) obj;
if (author == null) {
if (other.author != null)
return false;
} else if (!author.equals(other.author))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
class LearningBook extends Book{
private String subject;
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
#Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((subject == null) ? 0 : subject.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!super.equals(obj))
return false;
if (!(obj instanceof LearningBook))
return false;
LearningBook other = (LearningBook) obj;
if (subject == null) {
if (other.subject != null)
return false;
} else if (!subject.equals(other.subject))
return false;
return true;
}
}
class ReadingBook extends Book{
private int numberOfPages;
public int getNumberOfPages() {
return numberOfPages;
}
public void setNumberOfPages(int numberOfPages) {
this.numberOfPages = numberOfPages;
}
#Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + numberOfPages;
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!super.equals(obj))
return false;
if (!(obj instanceof ReadingBook))
return false;
ReadingBook other = (ReadingBook) obj;
if (numberOfPages != other.numberOfPages)
return false;
return true;
}
}
public class Runner {
public static void main(String[] args) {
Book learningBook = new LearningBook();
learningBook.setAuthor("auth");
learningBook.setName("sci");
Book learningBook2 = new LearningBook();
learningBook2.setAuthor("auth");
learningBook2.setName("sci");
Book readingBook = new ReadingBook();
readingBook.setAuthor("auth");
readingBook.setName("sci");
//returns false
System.out.println(learningBook.equals(readingBook) );
//returns true
System.out.println(learningBook.equals(learningBook2) );
}
}
Write an equals-implementation for each of the three classes. Every implementation is only responsible for its own fields.
The equals-implementations from the sub-classes ReadingBook and LearningBook should somewhere call super.equals() - the equals-implementation of Book.
You can ask the book instance for its class and check class equality.
book1.getClass().equals(book2.getClass())
You can use instanceof method to compare the type of the Object. To check if it is a type of LearningBook or ReadingBook example
Answer for your comment,
Lets say when you check the two instance it says they are different, then there is no issue you can return false. But if the instances are also same then you can check it with something like this after that
if (both instances are same) {
if (yourObjectIs instanceof LearningBook) {
you can check the two values of LearningBook here and return true if the are equals
} else {
you can check the two values of ReadingBook here and return true if the are equals
}
}
As it was mentioned you should overwrite equals(Object object) method. In your example you can do it like this:
public abstract class Book{
#NonNull protected String name;
#NonNull protected String author;
public Book(String name, String author) {
this.name = name;
this.author = author;
}
#Override
public boolean equals(Object object) {
if (object instanceof Book) {
var book = (Book) object;
return this.name.equals(book.name) && this.author.equals(book.author);
} else
return false;
}
}
public class LearningBook extends Book{
#NonNull private String subject;
public LearningBook(String name, String author,String subject) {
super(name, author);
this.subject = subject;
}
#Override
public boolean equals(Object object) {
if (object instanceof LearningBook) {
var book = (LearningBook) object;
return this.subject.equals(book.subject) && super.equals(book);
} else
return false;
}
}
public class ReadingBook extends Book{
#NonNull private int numberOfPages;
public ReadingBook(String name, String author,int numberOfPages) {
super(name, author);
this.numberOfPages = numberOfPages;
}
#Override
public boolean equals(Object object) {
if (object instanceof ReadingBook) {
var book = (ReadingBook) object;
return super.equals(book) && this.numberOfPages == book.numberOfPages;
} else
return false;
}
}
I've used #NonNull annotation to avoid NPE in equals method.

Comparing objects within a set

Right now I have this Java code
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
public class Dummy {
private String value;
public Dummy(final String value) {
this.value = value;
}
public boolean equals(final Object that) {
return that instanceof Dummy && Objects.equals(value, ((Dummy) that).value);
}
public int hashcode() {
return Objects.hash(value);
}
public static void main(final String... args) {
final Set<Dummy> dummies = new HashSet<>();
dummies.add(new Dummy("toto"));
System.out.println(dummies.contains(new Dummy("toto")));
}
}
The output is "false", and I'm supposed to change it to "true" by changing only one character, but I have absolutely no idea how to do that... Any ideas? Thanks. :)
hashcode() is not a Object's method but hashCode() is.
public int hashcode() {
return Objects.hash(value);
}
should be
public int hashCode() {
return Objects.hash(value);
}
In my case I had lots of objects who all need a equals and hash method. I have used lombok to reduce the work and code.
#Data
#AllArgsConstructor
#EqualsAndHashCode
public class JsonPictureStuff {
private String type;
private String url;
private String width;
private String height;
}
The annotations instruct lombok to add the code for you.
your set is not working because you are implementing equals and hashcode in a not proper way...
specially because you are not even considering the string field
you are avoiding the annotation Override that will hint you about wrongly named methods like hashcode and hashCode
you can do something like
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((value == null) ? 0 : value.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Dummy other = (Dummy ) obj;
if (value == null) {
if (other.value != null)
return false;
} else if (!value.equals(other.value))
return false;
return true;
}
in ide like eclipse you can do right click and auto generate those methods so you can save the implementation

Comparing two class objects with a method

I have a multitude of objects that are created with multiple instance variables (a string a multiple integers)
I have to create a method that will check for equality between the object that executes the method and another object. By that I mean that I would want to see whether all the instance variables are the same for two objects by calling a method. I'm thinking of something like the equals method (string1.equals(string2)), but can I do the same for an object with multiple instance variables which are not all strings?
example:
//object1
String name1= keyb.nextLine();
int age1= keyb.nextInt();
int monthborn1;
//object2
String name2=keyb.nextLine();
int age2 = keyb.nextInt();
int monthborn2;
I need to make a method that compare both objects and sees if they are equal or not.
Thank you very much.
Yes, you can create an equals method for your class. For example:
public final class Person {
private final String name;
private final int age;
private final int birthMonth;
public Person(String name, int age, int birthMonth) {
this.name = Objects.requireNonNull(name);
this.age = age;
this.birthMonth = birthMonth;
}
#Override
public boolean equals(Object o) {
if (o instanceof Person) {
Person rhs = (Person) o;
return name.equals(rhs.name)
&& age == rhs.age
&& birthMonth == rhs.birthMonth;
}
return false;
}
// Any time you override `equals`, you must make a matching `hashCode`.
// This implementation of `hashCode` is low-quality, but demonstrates
// the idea.
#Override
public int hashCode() {
return name.hashCode() ^ age ^ birthMonth;
}
}
In Java you usually have to manually check every field like this:
class MyObject {
String name;
int age, monthborn;
public boolean isEqual(MyObject other) {
return Objects.equals(name, other.name) &&
age == other.age && monthborn == other.monthborn;
}
}
Objects.equals is used here which is null-safe equivalent of name.equals(other.name). When you add new fields you will have to add new checks in your method as well. The alternative would be to utilize reflection, but it looks ugly and has significant performance drawback. Here's a draft example how to do this (do not take into account possible inheritance):
import java.lang.reflect.Field;
import java.util.Objects;
public class ObjectUtil {
public static <T> boolean allFieldsEqual(T o1, T o2) throws IllegalAccessException {
if(o1 == o2) return true;
if(o1.getClass() != o2.getClass()) return false;
for(Field field : o1.getClass().getDeclaredFields()) {
field.setAccessible(true);
if(!Objects.equals(field.get(o1), field.get(o2))) {
return false;
}
}
return true;
}
}
class MyObject {
String name;
int age, monthborn;
public boolean isEqual(MyObject other) {
try {
return ObjectUtil.allFieldsEqual(this, other);
} catch (IllegalAccessException e) {
return false;
}
}
}
This way upon adding the new field the isEqual method will take it into account as well. However I would not recommend such solution.
Please see this answer . Also, since you are overriding the equals method you should also override the hashcode method (also covered in the linked post)
Once you compare two objects you have to consider about all the members of the class.
We will assume that we have a class named B and with two member variables age and name.
class B :
public class B {
String name;
int age;
public B(String name, int age) {
this.age = age;
this.name = name;
}
}
Then lets compare the two different objects of same class using equals and hashcode methods which are inherited to our class B from Object class.
class A to compare :
public class A {
public static void main(String args[]){
B b1 = new B("a", 22);
B b2 = new B("a",22);
System.out.println(b1.equals(b2));
System.out.println(b1.hashCode());
System.out.println(b2.hashCode());
}
}
As soon as we compile and run class A we will get following out-puts.
false
705927765//returns int value
366712642//returns int value
But here in the class A we have passed same parameters to both objects of the B. Which means we are getting what we did not expect because of equals and hashcode methods which are in Object class not doing what we need.
So in this case we have to override these methods in our class B to make it success.
Class B after override done:
public class B {
String name;
int age;
public B(String name, int age) {
this.age = age;
this.name = name;
}
#Override
public boolean equals(Object obj) {
boolean flag = false;
if (obj instanceof B) {
B b = (B) obj;
if (this.age == b.age) {
if (this.name.charAt(0)==b.name.charAt(0))) {
flag = true;
} else {
flag = false;
}
} else {
flag = false;
}
} else {
flag = false;
}
return flag;
}
#Override
public int hashCode() {
return this.age+this.name.charAt(0);
}
}
If we run our class A again we could get follwing result :
true
119
119
Which means our work is done.

Equals method in sub classes

Here is the main class.
public class Person {
private String name;
public Person() {
name = "";
}
Its equal method is this
public boolean equals(Object otherObject) {
boolean isEqual = false;
if(otherObject != null && otherObject instanceof Person) {
Person otherPerson = (Person) otherObject;
if(this.name.equals(otherPerson.name))
isEqual = true;
}
return isEqual;
}
I have a subclass that extends the Person class.
public class Student extends Person {
private int studentNumber;
public Student() {
super();
studentNumber = 0;
}
How would I go about writing its equal method that will compare two object of type Student and will compare both variables. Name and studentNumber.
So far i have it similar to the Person class.
public boolean equals(Object otherObject) {
boolean isEqual = false;
if(otherObject != null && otherObject instanceof Student) {
Student otherPerson = (Student) otherObject;
if(this.studentnumber.equals(otherPerson.studentnumber))
isEqual = true;
}
return isEqual;
}
First of all: you need not test for null if you test instanceof; null is not instanceof anything. So, equals() for method can be simplified to:
public boolean equals(final Object obj)
{
if (!(o instanceOf Person))
return false;
final Person other = (Person) obj;
return name.equals(other.name);
}
THis means that in your Student class you may write your equals() method like this:
public boolean equals(final Object obj)
{
if (!super.equals(obj))
return false;
if (!(obj instanceof Student))
return false;
return studentnumber == other.studentnumber;
}
Note: do not forget hashCode().
You can use super.equals(). I have corrected some errors also with the studentNumber comparision.
public boolean equals(Object otherObject) {
boolean isEqual = false;
if (otherObject != null && otherObject instanceof Student) {
Student otherPerson = (Student) otherObject;
if (super.equals(otherObject) && this.studentNumber == otherPerson.studentNumber) {
isEqual = true;
}
}
return isEqual;
}

Using Objects as keys in Java HashMap

package test;
import java.util.HashMap;
public class test {
public static void main(String args[]) {
HashMap<ID, String> h = new HashMap<ID, String> ();
String b;
ID id1 = new ID(100);
ID id2 = new ID(200);
ID id3 = new ID(200);
h.put(id1, "apple");
h.put(id2, "pear");
**System.out.println(h.containsKey(id3));**
}
}
class ID {
Integer code;
public ID(Integer id) {
this.code = id;
}
#Override
public int hashCode()
{
return code.hashCode();
}
#Override
public boolean equals(Object o)
{
return this.code.equals(o);
}
}
the output of this program is "false". I can't understand of this result. I implemented hashCode and equals which are overrided. I don't have an idea how to solve. How to use objects as keys in java HashMap?
Your equals implementation is wrong :
public boolean equals(Object o)
{
return this.code.equals(o); // this will never return true,
// since you are comparing an Integer instance
// to an ID instance
}
You should compare this code to the other code:
public boolean equals(Object o)
{
if (!(o instanceof ID))
return false;
ID oid = (ID) o;
return this.code.equals(oid.code);
}
Wrong Implementation of equals method. According to your implementation it executes the Integer class equals method. We should handle the logic without execute library classes equals methods.
#Override
public boolean equals(Object o) {
if (o == null || ((o instanceof ID) && o.hashCode() != this.hashCode())) {
return false;
}
return true;
}

Categories