raedma
raedma

Reputation: 285

Lombok: Equals for string instance variables in subclass

I recently stumbled across Lombok and wanted to test it, when I ran into this slight problem.

Suppose I have

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.NonNull;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Name extends AbstractName {

    @NonNull
    private String firstname;

    @NonNull
    private String lastname;

}

where AbstractName is an empty abstract class. I want to test the equals method with

@Test
public void testEquals() {
    Name instance1 = new Name("Vorname","Nachname");
    Name instance2 = new Name("Vorname","Nachname");
    boolean expResult = true;
    boolean result = instance1.equals(instance2);
    assertEquals(expResult, result);
}

As you can see, I would expect the two instances of Name to be equal. In case you add

System.out.println(instance1.getFirstname().equals(instance2.getFirstname()));
System.out.println(instance1.getLastname().equals(instance2.getLastname()));

one gets

true
true

So, why does the test fail? Is it a hashCode() problem due to the inheritence of AbstractName? How can I fix that?

BTW, it does not fail, if Name is a standalone class and not a subclass of AbstractName.


Vanilla Java

This is code created by Lombok

@java.lang.Override
@java.lang.SuppressWarnings("all")
@lombok.Generated
public boolean equals(final java.lang.Object o) {
    if (o == this) return true;
    if (!(o instanceof Name)) return false;
    final Name other = (Name) o;
    if (!other.canEqual((java.lang.Object) this)) return false;
    if (!super.equals(o)) return false;
    final java.lang.Object this$firstname = this.getFirstname();
    final java.lang.Object other$firstname = other.getFirstname();
    if (this$firstname == null ? other$firstname != null : !this$firstname.equals(other$firstname)) return false;
    final java.lang.Object this$lastname = this.getLastname();
    final java.lang.Object other$lastname = other.getLastname();
    if (this$lastname == null ? other$lastname != null : !this$lastname.equals(other$lastname)) return false;
    return true;
}

Upvotes: 1

Views: 2052

Answers (2)

firstpostcommenter
firstpostcommenter

Reputation: 2931

So, why does the test fail? Is it a hashCode() problem due to the inheritence of AbstractName? How can I fix that? BTW, it does not fail, if Name is a standalone class and not a subclass of AbstractName

Elaborating regarding why the test would fail when callSuper is True.

Lets say you have a Child class which extends an abstract class. (or any other class for that matter)

Name.java:

@EqualsAndHashCode(callSuper = true)
public class Name extends AbstractName {

AbstractName.java:

public class AbstractName {
//some fields

Now, when we compare 2 instances of Name.java with same values then because we are overriding equals() and hashcode() methods using @EqualsAndHashCode lombok annotation in Name.java so the values are checked correctly. But since we are not overriding the equals() and hashcode() method in AbstractName.java so by default the equals() will always fail even if the values of properties are the same (unless the AbstractName's object instances are pointing to same object reference)

So, one option would be to make @EqualsAndHashCode(callSuper = false) in the Child class. But then the fields in the parent class (AbstractName.java) will not be compared

Solution:- Better option would be to add @EqualsAndHashCode(callSuper = true) in Name.java and @EqualsAndHashCode in AbstractName.java so that the equals() and hashcode() are overridden in both parent and child class so that the fields in all classes are compared correctly

Upvotes: 0

Andrew S
Andrew S

Reputation: 2801

Using @Data is a short cut which includes @EqualsAndHashCode. Try explicitly adding @EqualsAndHashCode and set callSuper to false.

However, for long-term maintenance, it may be safer to override equals/hashCode in the base class and just return true/0, or apply @EqualsAndHashCode to the base class.

Upvotes: 3

Related Questions