-1

I'm studying object oriented programming in Java at my school and I had to do an exercise to compare Circles.

I had the Circle Class with these

private int id;

private String bgColor;

private String fgColor;

And inside it I had to use the equals method to compare two circles (by using these three attributes): a circle is equal to other circle if its radius and the bg and fgColor are the same.

public boolean equals(Object obj) {
    boolean found;
    if (obj == null) {
        found = false;
    }
    if (getClass() != obj.getClass()) {
        found = false;
    }
    final Circle other = (Circle) obj;
    if (Double.doubleToLongBits(this.radius) == Double.doubleToLongBits(other.radius)) {
        //found = false;
        
        if (Objects.equals(this.bgColor, other.bgColor)) {
            //found = false;
            
            if (Objects.equals(this.fgColor, other.fgColor)) {
                return true;
            }//end if fgColor
            else{
                found = false;
            }   
        }//end if bgcolor
        else{
            found = false;
        }   
    }//end if radius
    else{
        found = false;
    }   

    return found;
}

But my teacher told me that the code above is "confusing", but I don't understand why.

Do you know a better solution?

My teacher wants that we folow this structure (this case is only comparing one property):

public boolean equals (Object obj)
{
      boolean b;

      if(obj == null)
      {
              b = false;
      }
      else
      {
              if(this == obj)//same object
              {
                      b = true;
              }
              else
              {
                      if(obj instanceof Book)                   
                      {
                              Book other = (Book) obj; 
                              b = (this.id == other.id); 
                      } 
                      else
                      {
                              b = false;
                      }
              }
      }

      return b;
}
Community
  • 1
  • 1
Jordi 45454
  • 275
  • 2
  • 3
  • 11
  • Yes it is. Very confusing. – Suresh Atta Dec 20 '15 at 17:29
  • 3
    @Tiny That would be about as confusing as his code above. – Kayaman Dec 20 '15 at 17:33
  • Instead of having separate `else` and `ifs`, use the `else if` construct. There's no advantage in having so many braces. – Kayaman Dec 20 '15 at 17:34
  • 3
    Am I the only one who dislikes "one return" per method in Java in 2015? For me it worsens the readability a lot, won't approve such a code at code-review without a good reason. (I am speaking of teacher's code) – user3707125 Dec 20 '15 at 17:36
  • I'm confused by `Double.doubleToLongBits`... Is there not `Double.equals`? Why couldn't you use `==` there? Same for the color strings. `bgColor.equals(other.bgColor)` is the correct way – OneCricketeer Dec 20 '15 at 17:40
  • @Tiny The OP explicitly states two objects are equal if the radius, bgColor and fgColor are equal – tddmonkey Dec 20 '15 at 17:48
  • @Tiny: quoting directly from the OP - "A circle is equals to other circle if its radius and the bg and fgColor are the same". The code you're referring to is an _example_ their teacher has provided – tddmonkey Dec 20 '15 at 17:52
  • @Tiny - are you actually being serious? The OP needs to compare their Circle class for equality and the Book class is provided as an _example_ of structure - the key part being structure – tddmonkey Dec 20 '15 at 17:57
  • Downvoting and flagging, this question should be posted on [CodeReview](http://codereview.stackexchange.com) and not here. – Pétur Ingi Egilsson Dec 20 '15 at 18:36
  • @user3707125 yes, returning early also simplifies conditionals (no need for `else`) – Caridorc Dec 20 '15 at 18:42

5 Answers5

3

This is about the most concise version (assuming that radius and colors can't be null). The null check for obj is taken care of by the instanceof test:

public boolean equals(Object obj) {

   if( ! (obj instanceof Circle ) )
       return false;

    Circle rhs = (Circle)obj;
    return Double.compare( radius, rhs.radius ) == 0 &&
        bgColor.equals( rhs.bgColor ) &&
        fgColor.equals( rhs.fgColor );
}
NietzscheanAI
  • 966
  • 6
  • 16
  • Whoever downvoted the original version of this from a couple of minutes ago: that was a slip of the `enter' key. I have every reason to believe this correct. Please be kind enough to see if you still think this deserves a downvote (and kindly say why). – NietzscheanAI Dec 20 '15 at 17:49
  • http://stackoverflow.com/questions/17898266/why-cant-we-use-to-compare-two-float-or-double-numbers – bhspencer Dec 20 '15 at 17:50
  • This won't compile. There is no variable "other". The parm is called obj. – bhspencer Dec 20 '15 at 17:52
  • Also you fail to check for null. You will get an NPE if obj is null. – bhspencer Dec 20 '15 at 17:52
  • @bhspencer instanceof takes care of the need to check obj for null. See "Effective Java" (or the spec). – NietzscheanAI Dec 20 '15 at 17:54
  • @bhspencer Double comparison now ammended according to your link. Thanks. – NietzscheanAI Dec 20 '15 at 17:54
  • I did the original downvote due to what appeared to be a blatant c+p from a deleted answer. It still stands as your code has other issues; most notable allowing subclasses and potential NPEs – tddmonkey Dec 20 '15 at 17:55
  • @MrWiggles - NPEs are catered for in the comments: no sensible implementor of Circle would allow a constructor with null values for the colors. What do you mean by `allowing subclasses'? The instanceof check is correct (and indeed the idiomatic way of implementing equals) - it's basically equivalent to the if( getClass().equals ...) of other answers. – NietzscheanAI Dec 20 '15 at 17:57
  • 1
    `instanceof` is idiomatic for final classes/equals methods, but violates the equality contract otherwise. Without further clarification about whether the `bgColor` and fgColor` are mandatory you shouldn't make that assumption. – tddmonkey Dec 20 '15 at 18:02
  • I agree with @MrWiggles here, you cannot assume that the color strings are never null unless that is stipulated in the spec. – bhspencer Dec 20 '15 at 19:26
  • @MrWiggles - Strictly, you are correct about inheritance. However, catering for that correct requires more than just the getClass check in general: https://www.artima.com/lejava/articles/equality.html, so I figured that (given the OP's level of Java knowledge) that the simplified version I posted was more appropriate. – NietzscheanAI Dec 20 '15 at 20:24
  • Also I think your earlier use of == was ok in this particular instance and the use of Double.compare is unnecessary (possibly even undesirable). Check the API doc on the differences between Double.compare and ==. – Klitos Kyriacou Dec 20 '15 at 22:01
0

If you are using a IDE (I hope you do) probably it has an option to generate code for equals method. Eclipse generates something like:

@Override
public boolean equals(Object obj) {
    if (this == obj)
        return true;
    if (obj == null)
        return false;
    if (getClass() != obj.getClass())
        return false;
    Circle other = (Circle) obj;
    if (bgColor == null) {
        if (other.bgColor != null)
            return false;
    } else if (!bgColor.equals(other.bgColor))
        return false;
    if (fgColor == null) {
        if (other.fgColor != null)
            return false;
    } else if (!fgColor.equals(other.fgColor))
        return false;
    if (Double.doubleToLongBits(radius) != Double.doubleToLongBits(other.radius))
        return false;
    return true;
}

And don't forget implements hashcode method when you implements equals method and vicecersa.

eltabo
  • 3,749
  • 1
  • 21
  • 33
  • The spec does not call for comparing IDs "A circle is equals to other circle if its radius and the bg and fgColor are the same." – bhspencer Dec 20 '15 at 17:47
0

Rather than having a single return statement consider using multiple return points to simplify the code. This way you do not need extra boolean variables to hold on to the results of prior conditions.

public class Circle {
    public double radius;
    public String bgColor;
    public String fgColor;

    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        } else if (obj instanceof Circle) {
            Circle other = (Circle) obj;
            if (Double.compare(this.radius, other.redius) == 0 
                    && compareStrings(this.fgColor, other.fgColor)
                    && compareStrings(this.bgColor, other.bgColor)) {
                return true;
            } else {
                return false;
            }
        } else {
            return false;
        }
    }

    private boolean compareStrings(String a, String b) {
        if (a == null && b == null) {
            return true;
        } else if (a != null) {
            return a.equals(b); 
        } else if (b != null) {
            return b.equals(a);
        }
        return false;
    }   
}

This solution allows for the possibility that either of the String fgColor or bgColor might be null without throwing a NPE. The String comparison has been extracted into its own function to aid readability and reduce confusion.

bhspencer
  • 13,086
  • 5
  • 35
  • 44
  • If `fgColor` or `bgColor` are null your code will throw a `NullPointerException` – tddmonkey Dec 20 '15 at 17:50
  • Fixed to avoid possible NPE if color strings are null. – bhspencer Dec 20 '15 at 19:26
  • The instance of test caters for null, so the initial test is redundant. – NietzscheanAI Dec 20 '15 at 20:26
  • if (Double.compare(this.radius, other.redius) == 0 && compareStrings(this.fgColor, other.fgColor) && compareStrings(this.bgColor, other.bgColor)) { return true; } else { return false; } can be reduced to: return Double.compare(this.radius, other.redius) == 0 && compareStrings(this.fgColor, other.fgColor) && compareStrings(this.bgColor, other.bgColor); – NietzscheanAI Dec 20 '15 at 20:43
  • @user217281728 reducing the number of lines taken up by a piece of code is not always desirable, sometimes the more compact code is less readable. I would argue that evaluating a condition that contains 3 function calls and 3 boolean operators in the same statement as the return does not enhance readability. – bhspencer Dec 20 '15 at 22:48
0
public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        // its a Circle so its safe to case
        Circle other = (Circle)obj;

        // equals ONLY if 3 conditions are met
        if (radius == other.getRadius() &&
            bgColor.equals(other.getBgColor()) &&
            fgColor.equals(other.getFgColor())){
            return true;
        }
        return false;
    }
chenchuk
  • 5,324
  • 4
  • 34
  • 41
  • This will throw an NPE if bgColor is null. Also consider http://stackoverflow.com/questions/17898266/why-cant-we-use-to-compare-two-float-or-double-numbers – bhspencer Dec 20 '15 at 18:09
0

As a follow-up to my previous answer:

Writing an equals method that works correctly in the presence of subclassing is extremely non-trivial (see Joshua Bloch's comments in Item 8 of `Effective Java').

Indeed, until relatively recently the was no widely known single method for doing this.

In 2009, the article "How to Write an Equality Method in Java" by Martin Odersky, Lex Spoon, and Bill Venners shows that this can be achieved in terms of a `canEqual' method.

NietzscheanAI
  • 966
  • 6
  • 16
  • 1
    This should be a comment and not an answer. – bhspencer Dec 20 '15 at 22:50
  • 1
    @bhspencer It seems a bit lengthy for that. Also, the comments under my previous answer were getting to the 'recommended you continue in chat' length. – NietzscheanAI Dec 21 '15 at 06:14
  • This should be edited into your original answer, since this answer does not provide an answer to the question on its own. – Cypher Jan 25 '16 at 17:47