3

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.

R.DM
  • 105
  • 1
  • 11

4 Answers4

4

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;
    }
}
C. K. Young
  • 219,335
  • 46
  • 382
  • 435
  • I'd probably either make `Person` `final` or make both `equals` and `hashCode` `final` to make it impossible to subclass `Person` and violate symmetry. – Paul Boddington Nov 12 '15 at 03:56
  • @Chris Jester-Young♦ Thank you for the help! Would you mind explaining why do we need a hashcode after overriding? I'm still new at programming.. – R.DM Nov 12 '15 at 16:20
  • 1
    @R.DM The hash code is used when using your object as a hash key. The "contract" of hash codes is that equal objects (all objects `a` and `b` where `a.equals(b)` returns true) must have the same hash code. If you don't override `hashCode`, the default hash code is the "identity hash code", which is different for each object, even when they compare equal using `equals`. That's why it's important for `hashCode` to compare exactly the same fields as `equals` does. – C. K. Young Nov 12 '15 at 17:32
  • 1
    @PaulBoddington Thanks, I added `final` to the fields too so the hash code is invariant. – C. K. Young Nov 12 '15 at 17:36
2

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.

Tagir Valeev
  • 97,161
  • 19
  • 222
  • 334
  • I went the opposite way in my answer: my constructor has an explicit `Objects.requireNonNull` call in it. That way, `equals` and `hashCode` don't need to worry about nulls. – C. K. Young Nov 12 '15 at 03:49
  • 1
    @ChrisJester-Young, that depends on user needs, though probably for the `name` field `requireNonNull` *usually* better fits the business logic. – Tagir Valeev Nov 12 '15 at 03:52
0

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)

Community
  • 1
  • 1
aarbor
  • 1,398
  • 1
  • 9
  • 24
0

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.

Madushan Perera
  • 2,568
  • 2
  • 17
  • 36
  • Why would you only compare the first character of the name? Also, all the nested `if`s could be done away with more extensive use of `&&`. – C. K. Young Nov 12 '15 at 17:34