The equals() Method in Java: Comparing Objects and Overriding equals() - Quiz

Total: 5 questions

1. 

What is the difference between == and equals() in Java?

For reference types, == checks identity: whether both variables point to the same object in memory. The equals() method checks logical equality: whether the objects mean the same thing, as defined by the class.

String s1 = new String("Java");
String s2 = new String("Java");
System.out.println(s1 == s2);      // false - different objects
System.out.println(s1.equals(s2)); // true  - same characters

For primitives (int, double, boolean) only == is used, and the values themselves are compared. == is also the right choice for enum constants and when you really need to check that two references are the same. Compare strings, wrappers, dates and your own classes with equals() or Objects.equals().

2. 

How do you correctly override the equals() method in your own class?

The implementation in Object simply returns this == obj, so it compares references. To compare objects by their fields, override the method with the equals(Object o) signature and the @Override annotation. The typical steps:

@Override
public boolean equals(Object o) {
    if (this == o) return true;                              // 1
    if (o == null || getClass() != o.getClass()) return false; // 2
    Person person = (Person) o;                              // 3
    return age == person.age                                 // 4
            && Objects.equals(fullName, person.fullName);
}

1) a quick same-reference check; 2) null or a different class returns false; 3) a safe cast; 4) compare the significant fields: primitives with ==, references with Objects.equals(), double/float with Double.compare()/Float.compare(). Always override hashCode() together with equals(), using the same fields. IntelliJ IDEA generates both methods via Code → Generate (Alt+Insert).

3. 

What properties make up the equals() contract?

For non-null references x, y and z, every equals() implementation must satisfy five properties:

  • Reflexive: x.equals(x) is always true.
  • Symmetric: x.equals(y) returns the same result as y.equals(x).
  • Transitive: if x.equals(y) and y.equals(z), then x.equals(z).
  • Consistent: repeated calls return the same result as long as the fields used in the comparison don't change.
  • Non-null: x.equals(null) is always false and doesn't throw an exception.

HashMap, HashSet, ArrayList.contains() and other collections rely on these properties, so breaking them leads to hard-to-find bugs. On top of that, objects that are equal by equals() must have the same hashCode().

4. 

How does Objects.equals() work, and why use it instead of a.equals(b)?

Calling a.equals(b) throws a NullPointerException if a is null. The utility method java.util.Objects.equals(a, b) (Java 7+) handles null for you: if both arguments are the same reference (including both null), it returns true; if only one is null, it returns false; otherwise it returns a.equals(b).

String name = null;
// name.equals("Ivan");                 // NullPointerException
System.out.println("Ivan".equals(name));          // false
System.out.println(Objects.equals(name, "Ivan")); // false
System.out.println(Objects.equals(null, null));   // true

Putting the literal on the left also avoids the NPE, but it only works when one side is known to be non-null. Objects.equals() works in every case, which is why it's the recommended way to compare reference fields inside your own equals(). For arrays, there's Objects.deepEquals().

5. 

Should you use getClass() or instanceof for the type check in equals()?

getClass() != o.getClass() treats only instances of exactly the same class as equal: a subclass never equals its parent, but symmetry is guaranteed. o instanceof Person lets subclasses through too. That's natural for final classes, but if a subclass adds fields and overrides equals(), you can end up with person.equals(employee) == true and employee.equals(person) == false, which breaks symmetry.

Since Java 16, pattern matching combines the check and the cast, and a separate null check isn't needed because null instanceof Person is always false:

if (!(o instanceof Person other)) return false;

For simple data carriers, a record is more convenient: the compiler generates equals(), hashCode() and toString() from all its components.

Page 1 of 1