Skip to content

[FEATURE] @EqualsAndHashCode for records #3246

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
pawelkedra opened this issue Aug 15, 2022 · 10 comments
Open

[FEATURE] @EqualsAndHashCode for records #3246

pawelkedra opened this issue Aug 15, 2022 · 10 comments

Comments

@pawelkedra
Copy link

pawelkedra commented Aug 15, 2022

Describe the feature
Yes, I know - equals() and hashCode() are automatically generated for records, but there's no easy way to customise them. Sometimes you don't want to compare all object properties, e.g. in case of DB entities it's usually enough to implement equals/hashCode using only the ID. In case of classes, it's very easy to customise it with Lombok:

@EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class MyEntity {
    @Id
    @EqualsAndHashCode.Include
    private UUID id;
    private String name;     
}

Unfortunately, I can't do the same with records - they are not supported by Lombok's @EqualsAndHashCode annotation. This piece of code fails to compile with java: @EqualsAndHashCode is only supported on a class. error:

@EqualsAndHashCode(onlyExplicitlyIncluded = true)
public record MyEntity (
    @Id 
    @EqualsAndHashCode.Include
    UUID id,
    String name
) {}

I think it would be great to have this annotation also for records, and throw the compilation error only if @EqualsAndHashCode.Include/Exclude is not used in the code.

Describe the target audience
Anyone who migrates POJO classes to Java records, but has specific requirements regarding equals() and hashCode() methods.

@swaranga
Copy link

I understand the intent of the request to have Lombok generate the equals and hashcode via annotations but it is not accurate to say there is no way to customize the equals and hashcode method for record classes. You can definitely overridde them and implement your own logic

public record MyEntity(UUID id, String name) {
    @Override
    public boolean equals(Object o) {
        return o instanceof MyEntity other &&
                   Objects.equals(this.id, other.id);
    }

    @Override
    public int hashCode() {
        return Objects.hashCode(this.id);
    }
}

@pawelkedra
Copy link
Author

pawelkedra commented Sep 11, 2022

Well, yes, but that's true also for classes, which are supported by @EqualsAndHashCode.

@swaranga
Copy link

Agreed. I was only responding to your comment about record classes:

but there's no way to customize them.

Not disputing the utility of the feature request itself.

@pawelkedra
Copy link
Author

Updated the description: but there's no easy way to customize them.

@datze
Copy link

datze commented May 30, 2023

There is another reason for a @EqualsAndHashCode annotation on a record: When the record contains an array, I would expect the Lombok-generated equals() and hashcode() methods to use the arrays values for comparison (with Arrays.equals() instead of o1.equals(o2)).

@TheConen
Copy link

TheConen commented Jun 1, 2023

Exactly that. After an update from Sonar 8 LTS to Sonar 9 LTS I had some new findings about https://rules.sonarsource.com/java/RSPEC-6218 and was expecting to be able to use @EqualsAndHashCode for that - and was very surprised when that was not possible.

The same goes for @ToString

@luneo7
Copy link

luneo7 commented Mar 22, 2024

One other use for that is oracle/graal#4348 ... Where in native image right now at least records own implementation lacks performance since they are doing reflection... I know that they will be improving it in a new GraalVM release, but a "handwritten" method (@ToString as well) will definitely improve performance.
It would be good to support records with @EqualsAndHashCode and @ToString as well...

@ukv001
Copy link

ukv001 commented Jun 10, 2024

Another place where it would be good to have the support of the annotations is the exclude. e.g. when writing a test, using mockito to verify the content of an instance that is called on a specific method, where a timestamp is included, then the equals.exclude would be very useful.

@cerebro84
Copy link

cerebro84 commented Dec 11, 2024

Another place where it would be good to have the support of the annotations is the exclude. e.g. when writing a test, using mockito to verify the content of an instance that is called on a specific method, where a timestamp is included, then the equals.exclude would be very useful.

@ukv001 For that you don't normally want to override equals (modifying production code for the sake of the tests), you can use assertJ and, to make sure you really compare the fields and you don't rely on the implemented equals, use .usingRecursiveComparison() combined with .ignoringFields("nameOfTheField"). Unless the comparison of the timestamp is something you don't want either in production code when checking equality.

@q3769
Copy link

q3769 commented Mar 24, 2025

Another place where it would be good to have the support of the annotations is the exclude. e.g. when writing a test, using mockito to verify the content of an instance that is called on a specific method, where a timestamp is included, then the equals.exclude would be very useful.

@ukv001 For that you don't normally want to override equals (modifying production code for the sake of the tests), you can use assertJ and, to make sure you really compare the fields and you don't rely on the implemented equals, use .usingRecursiveComparison() combined with .ignoringFields("nameOfTheField"). Unless the comparison of the timestamp is something you don't want either in production code when checking equality.

Testing convenience may or may not be a proper usage for this, but the main point of requesting @EqualsAndHashCode and @ToString to support record types is the customization of Include/Exclude. (Otherwise, if there is no Include/Exclude need, then the Java record's default implementation should be preferred.)

IMHO, though, when supporting the Include/Exclude, the Lombok implementation should be careful, and NOT to change anything (at least semantically) from the implementation that come with Java record. e.g. if the default Java record equals() compares array fields with ==, then Lombok equals() should not implement it any other way, at least not by default. Otherwise, such inconsistent implementations would be a "surprise" and may have very unwelcome effects.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants