javaequalsjava-record

java record with mutable properties (like set or list) with equals()


I know, Java records are value based, i.e. by default two records are equal if both records have the same values (compared to Java classes where by default two classes are equal if their memory addresses are the same).

Am I right with the following assumption that this:

public record User(String name, Set<String> hobbies){}

is the same as this:

public record User(String name, Set<String> hobbies) {
        
        @Override
        public int hashCode() {
            return Objects.hash(name, hobbies);
        }

        @Override
        public boolean equals(Object obj) {

            // check for "memory equality" (also in java records???)
            if (this == obj) return true; 
            if (obj == null || getClass() != obj.getClass()) return false;

            // check for "value equality"
            final User other = (User) obj;
            return Objects.equals(name, other.name) 
                    && Objects.equals(hobbies, other.hobbies) ;
        }
    }

My question is:

Is my Equals/HashCode implementation exactly the same as the automatic/generated Equals/HashCode implementation? If I do not provide an Equals/HashCode, then Java records adds a default one, but will it also include the check for "memory equality" or only does it only check for "value equality"?


Solution

  • This is implementation dependent.

    If you take a look at the Javadoc of Record#equals:

    Apart from the semantics described above, the precise algorithm used in the implicitly provided implementation is unspecified and is subject to change

    and Record#hashCode:

    The precise algorithm used in the implicitly provided implementation is unspecified and is subject to change within the above limits.

    So, implementations are free to implement it however they want as long as they only use the components (and use all components).

    However, most implementations will probably add a check for reference equality (==) in equals so that the other checks don't need to be done in case the objects are the same but you shouldn't rely on that.

    The null check and the check that the argument is of the same type are necessary to obey to the contract of Object#equals but the latter could also be done using an instanceof-check or something implemented natively.