5

I have a bean with 4 attributes:

user
institutionId
groupId
postingDate

I use Eclipse to generate equals and hashcode but the resulting code is not pretty. Is there a compact way to do the same? Assuming I want equals & hashcode to use all the attributes or a subset of them.

@Override
public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((groupId == null) ? 0 : groupId.hashCode());
    result = prime * result + ((institutionId == null) ? 0 : institutionId.hashCode());
    result = prime * result + ((postingDate == null) ? 0 : postingDate.hashCode());
    result = prime * result + ((user == null) ? 0 : user.hashCode());
    return result;
}

@Override
public boolean equals(Object obj) {
    if (this == obj)
        return true;
    if (obj == null)
        return false;
    if (getClass() != obj.getClass())
        return false;
    ManGroupKey other = (ManGroupKey) obj;
    if (groupId == null) {
        if (other.groupId != null)
            return false;
    } else if (!groupId.equals(other.groupId))
        return false;
    if (institutionId == null) {
        if (other.institutionId != null)
            return false;
    } else if (!institutionId.equals(other.institutionId))
        return false;
    if (postingDate == null) {
        if (other.postingDate != null)
            return false;
    } else if (!postingDate.equals(other.postingDate))
        return false;
    if (user == null) {
        if (other.user != null)
            return false;
    } else if (!user.equals(other.user))
        return false;
    return true;
}
Lluis Martinez
  • 1,963
  • 8
  • 28
  • 42
  • What do you mean, "compact way"? It depends on what your requirements for `equals` is. If you don't need it all, delete some. IIRC there are some reflective ones as well, alternate generators, and ways to choose which fields are used. – Dave Newton Mar 14 '14 at 16:39
  • Sonar reports a lot of "If Stmts Must Use Braces" warnings. Hashcode is fine, equals is very difficult to read. – Lluis Martinez Mar 14 '14 at 16:39
  • @LluisMartinez Reformat it. – Dave Newton Mar 14 '14 at 16:40
  • 2
    Take a look at `Objects.equals` and `Objects.hash` in `java.util` for Java >= 7, otherwise google guava has those helpers. – Leonard Brünings Mar 14 '14 at 16:40
  • I'm stuck with Java 5 and can't use Guava, but that's the way I'd like. – Lluis Martinez Mar 14 '14 at 16:42
  • The implementation provided by netbeans is correct if you consider that you object is an entity identificable by those instance attributes. You could als take a look at : http://stackoverflow.com/questions/17027777/relationship-between-hashcode-and-equals-method-in-java, for further theoretical background on this matter, but for a pragmatic point of view this is just correct. – Victor Mar 14 '14 at 16:43
  • I usually remove the auto-generated `null` checks, when I'm sure that my class permits no `null` values for the attributes in question (e.g. by checking in constructor and having immutable properties). This saves a considerable amount of generated code. – qqilihq Mar 14 '14 at 16:46
  • Guava _does_ have a Java 5 backport... – Louis Wasserman Mar 14 '14 at 18:17

6 Answers6

6

In Java 7


public int hashCode() {
    return Objects.hash(groupId, institutionId, postingDate, user);
}


public boolean equals(Object obj) {
    if (this == obj)
        return true;
    if (obj == null)
        return false;
    if (getClass() != obj.getClass())
        return false;

    // cast to correct class
    Target o = (Target)obj;

    return Objects.equals(groupId, o.groupId) &&
           Objects.equals(institutionId, o.institutionId) &&
           Objects.equals(postingDate, o.postingDate) &&
           Objects.equals(user, o.user);
}
Leonard Brünings
  • 12,408
  • 1
  • 46
  • 66
5

You could compact the code down, but the odds are far higher that you would introduce bugs than that you would do anything useful. All the parts of the equals and hash code method are there for a reason.

If it's bothering you most IDEs have a folding editor, just click the little yellow box (usually) and all the contents of the method get hidden away.

Tim B
  • 40,716
  • 16
  • 83
  • 128
4

Instead of using the eclipse generated code, you can use Apache-common-langs(http://commons.apache.org/proper/commons-lang/) class HashCodeBuilder and EqualsBuilder to do this:

public int hashCode() {
   return HashCodeBuilder.reflectionHashCode(this);
}


public boolean equals(Object obj) {
   return EqualsBuilder.reflectionEquals(this);
 }
WarriorOfKnight
  • 86
  • 1
  • 1
  • 6
  • 2
    Although there are some possible drawbacks: 1. reflection is slower, 2. the order is not really under your control, 3. all fields are used (which might or might not be what you want) – Puce Mar 14 '14 at 16:54
  • I pick this one because: a) I can't use Java 7 (or Guava) b) performance is not an issue (not thousands of objects) and c) in this case I use all fields. – Lluis Martinez Mar 14 '14 at 17:12
2

hashCode: Either:

@Override 
public int hashCode() {
    return Objects.hash(user, institutionId, groupId, postingDate);
}

Or:

@Override 
public int hashCode() {
    int result = 17;
    result = 31 * result + Objects.hashCode(user);
    result = 31 * result + Objects.hashCode(institutionId);
    result = 31 * result + Objects.hashCode(groupId);
    result = 31 * result + Objects.hashCode(postingDate);
    return result;
}

Equals:

public boolean equals(Object obj){
    if (obj == this){
        return true;
    }
    if (! (obj instanceof ManGroupKey)){
        return false;
    }
    ManGroupKey other = (ManGroupKey) obj;
    return Objects.equals(user, other.user)
           && Objects.equals(institutionId, other.institutionId)
           && Objects.equals(groupId, other.groupId)
           && Objects.equals(postingDate, other.postingDate);
}
Puce
  • 37,247
  • 13
  • 80
  • 152
  • return Objects.equals(user, other.user). I cannot edit myself, since 6 characters must be changed, "user " got just four :) – badbishop Nov 25 '17 at 19:03
1

You can at least remove one level of nesting by removing the other.x != null check.

Comparing a value in this way: x.equals(y) will always return false when y is null.

Aside from that: the .equals() method is a good example where a bit of reflection can be handy, possible extracted out into a generic utility method. All you have to do is run through the different fields and see if they're equal in the two objects, that can be done in a few lines.

Obviously that is only feasible when you actually want to compare each field (or you'll have to add some additions to it which let you choose the fields).

Jeroen Vannevel
  • 43,651
  • 22
  • 107
  • 170
  • 1
    But then you won't return true if both self and other have x as null. – Tim B Mar 14 '14 at 16:41
  • @TimB: if you invert the checks and instead use `if(this.postingDate != null) { if(!(this.postingData.equals(other.postingData))}` then this should not be a problem, right? Just return `false` for every intermediate check and return `true` at the very end as fallback. – Jeroen Vannevel Mar 14 '14 at 16:45
1

I think the library, that can suite you is apache common. It provides EqualsBuilder and HashCodeBuilder classes, that do exactly what you are looking for.

Consider this question for details: Apache Commons equals/hashCode builder

Here are some code snippets:

public class Bean{

    private String name;
    private int length;
    private List<Bean> children;


    @Override
    public int hashCode(){
        return new HashCodeBuilder()
            .append(name)
            .append(length)
            .append(children)
            .toHashCode();
    }

    @Override
    public boolean equals(final Object obj){
        if(obj instanceof Bean){
            final Bean other = (Bean) obj;
            return new EqualsBuilder()
                .append(name, other.name)
                .append(length, other.length)
                .append(children, other.children)
                .isEquals();
        } else{
            return false;
        }
    }
}
Community
  • 1
  • 1
Boris Brodski
  • 8,425
  • 4
  • 40
  • 55