Overriding equals() and hashCode()

Figure 469. Shape.equals() Slide presentation
public abstract class Shape {
  ...
  @Override  public boolean equals(final Object o) {
    if (o instanceof Shape s ) { 
      return x == s.x && y == s.y; 
    } else {
      return false; 
    } ...

Promise: The current method overrides a superclass method.

Other instance is a Shape object?

Return true if and only if both center coordinate pairs are equal.

Other object distinct from class Shape.


Figure 470. Rectangle.equals() Slide presentation
public class Rectangle extends Shape {
  ...
  @Override public boolean equals(final Object o) {
    if (o instanceof Rectangle r) {
      return super.equals(o)  &&
             width == r.width && height == r.height ;
    } else {
      return false;
    } ...

Including superclass method Shape.equals().

Return true if and only if both width and height pairs are equal.


exercise No. 161

Let me pass, please!

Q:

Consider the following snippet:

final Scanner scan = new Scanner(System.in));
do {
  System.out.print("Please enter password: ");
  final String password = scan.nextLine();
  if (password == "secret") {
    break;  // Leave enclosing do ... while loop
  } else {
    System.out.println("Sorry, please try again");
  }
} while (true);
  System.out.println("You made it!");
  // ...

Describe the above code's intended behaviour. Will it succeed? Execute the above code and provide a clue for correcting the underlying flaw.

A:

The user is being asked for a password. Only when entering "secret" access shall be granted.

Unfortunately comparing the user's input and the corresponding password is seriously flawed:

...
  if (password == "secret")
...

On execution the string literal "secret" will be represented as a String object in memory. Each new user input string will be represented as a different String object in memory as well.

Unfortunately the == operator only works as expected for the eight built in primitive Java types. With respect to class instances variables hold references to objects rather than to the objects' values. The == operator compares for object identity rather than for object equality.

Two different String instances may off course be equal with respect to their payload namely the values they both represent. Comparing for object equality rather than for object identity in Java requires using the equals(...) method instead. A prerequisite for this to work requires the class authors overriding the Object.equals(Object o) method accordingly. This override does exist in class String. Within the given context we may thus simply use String.equals(Object o):

final Scanner scan = new Scanner(System.in));
do {
  System.out.print("Please enter password: ");
  final String password = scan.nextLine();
  if (password.equals("secret")) {
    break;  // Leave enclosing do ... while loop
  } else {
    System.out.println("Sorry, please try again");
  }
} while (true);
System.out.println("You made it!");
// ...

exercise No. 162

Why is == correctly comparing enum instances?

Q:

The preceding exercise Let me pass, please! told us to override Object.equals(Object o) when comparing different objects for equality. The == operator on the other hand does compare for object identity rather than semantic equality of objects based on their respective attribute values. However comparing Enum instances for equality using the == operator seems to be sufficient e.g.:

Day day = Day.Saturday;

// Sufficient using operator == ?
if (day == Day.FRIDAY) ... 

// Or do we require equals?
if (day.equals(Day.FRIDAY)) ...

Do we thus require an equals(Object o) method in enum Day for comparisons? Give a precise explanation.

A:

In case of enum instances using the == operator is sufficient. Due to the way enum's are being constructed instance creation is limited to the underlying enum's scope by its implicitly private constructor. It is thus impossible creating new instances outside the respective enum's scope.

Therefore for any two given enum instances both object identity and equality of their respective values is being guaranteed to yield the very same result:

MyEnum instance1 = MyEnum.x,                      // MyEnum representing
       instance2 = MyEnum.y;                      // some enum type.

final boolean
  objectIdentity = (instance1 == instance2),        // Both boolean values will
  objectEquality = instance1.equals(instance2);     // always be equal.

As an aside: In case of null values using the == operator avoids java.lang.NullPointerException problems:

Day day = null;

if (day == Day.FRIDAY) ...      // Just different, no problems

if (day.equals(Day.FRIDAY)) ... // Oops: NPE approaching ...