1

I have been working on a PlayingCard class, and I am having difficulty writing an equals() method for the class. The intent for the equals method is to compare two playing cards to see if they are identical or not.

I have used an example from the Big Java Late Objects Book and altered it to try and check to see if the two cards are identical or not, but in both cases (identical and not identical), I get the same output. What is going wrong and how can I get it to work?

This is the PlayingCard class, with the equals method at the bottom.

public class PlayingCard
{

private Rank rank;
private Suit suit;

public PlayingCard(Rank rank, Suit suit)
{
    this.rank = rank;
    this.suit = suit;
}

public Rank getRank()
{   
    System.out.println(rank);
    return rank;
}

public Suit getSuit()
{
    System.out.println(suit);
    return suit;
}

@Override
public String toString()
{
  return getClass().getName() + "[rank " + rank + "suit " + suit + "]"; 
}

public void format()
{
    System.out.format(rank + " OF " + suit);
    System.out.println("");
}

@Override
public boolean equals(Object otherObject)
{
    if (otherObject == null)
    {
        System.out.println("Match");
        return false;
    }
    if (getClass() != otherObject.getClass())
    {
        System.out.println("Match");
        return false;
    }

    System.out.println("No Match, True");
    PlayingCard other = (PlayingCard) otherObject;
    return suit.equals(other.suit) && rank == other.rank;
}

}

And this is the current tester:

public class PlayingCardTester 
{
public static void main(String[] args) 
{
    PlayingCard test = new PlayingCard(Rank.ACE, Suit.DIAMONDS);
    PlayingCard test2 = new PlayingCard(Rank.FIVE, Suit.CLUBS);
    PlayingCard test3 = new PlayingCard(Rank.ACE, Suit.DIAMONDS);
    test.getRank();
    test2.getRank();
    test.getSuit();
    test2.getSuit();
    test.toString();
    test.format();
    test2.toString();
    test2.format();
    test.equals(test2);
    test.equals(test3);
    System.out.println("");
}

}

Edit:

Enum Rank:

public enum Rank 
{
TWO(2), THREE(3), FOUR(4), FIVE(5), SIX(6), SEVEN(7), EIGHT(8), NINE(9), 
TEN(10), JACK(11), QUEEN(12), KING(13), ACE(14);

private int value;

private Rank(int value)
{
    this.value = value;
}

public int getValue()
{
    return value;
}
}

Enum Suit:

public enum Suit 
{
SPADES(-2), CLUBS(-1), HEARTS(0), DIAMONDS(1);

private int value;

private Suit(int value)
{
    this.value = value;
}

public int getValue()
{
    return value;
}
}

And the output I get from the tester:

run:
ACE   // getRank and getSuit print out the Rank and Suit respectivly for 
FIVE  // for the card specified and return it
DIAMONDS
CLUBS
ACE OF DIAMONDS // toString and format work together to get output of
FIVE OF CLUBS   // cards to show what they are
No Match, True  // This is from the equals method, both outputs are the
No Match, True  // same, first test-test2, second test-test3.

BUILD SUCCESSFUL (total time: 1 second)

What I am trying to do is to compare PlayingCard test with test2 and test3 to see if they are the same card or not, as in, having the same suit and rank or not, and printing out if they are equal or not.

Edit 2: Current equals method alteration, still just getting false as an output for both tests.

    @Override
public boolean equals(Object otherObject)
{
    boolean set = false;
    if (!(otherObject instanceof PlayingCard))
    {
        set = false;
    }

    if (otherObject == this)
    {
        set = true;
    }
    System.out.println(set);
    return set;
}
SIHB007
  • 69
  • 1
  • 10

3 Answers3

1

This is not doing quite what you may think:

if (getClass() != otherObject.getClass())

You are comparing Class object references here. Even if the Classes are of the same type, they may have different Class object references returned by their respective getClass() methods. Better yet, use the instanceof operator.

@Override
public boolean equals(Object otherObject)
{
    if (otherObject == null)
    {
        System.out.println("Match");
        return false;
    }
    if (otherObject instanceof PlayingCard)
    {
        PlayingCard other = (PlayingCard) otherObject;
        return suit.equals(other.suit) && rank == other.rank;
    }

    return false;
}

A good example I'd recommend looking at is the Java String equals() method. The implementation there is perfect. Follow it as a guideline:

public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String anotherString = (String) anObject;
        int n = value.length;
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            while (n-- != 0) {
                if (v1[i] != v2[i])
                        return false;
                i++;
            }
            return true;
        }
    }
    return false;
}

Also, I'd recommend you remove the print statements unless you are only doing it for debug purposes. The equals() comparison should only be comparing, not doing anything else.

Kon
  • 10,702
  • 6
  • 41
  • 58
  • 1
    It is not good practice to use `instanceof` to check types in an `equals` method, unless the method is declared `final`. Otherwise a class that overrides that method will always give you an asymmetric `equals` relationship (like the relationship between `java.util.Date` and `java.util.Timestamp`, which is horribly broken). – khelwood Feb 20 '15 at 16:04
  • 2
    @khelwood, this problem is an inherent problem in object oriented programming. Using `getClass()` breaks polymorphism, using `instanceOf` breaks the equals contract. The best is not to extend value classes unless the extension is only implementation and not value. – RealSkeptic Feb 20 '15 at 16:06
  • I have just altered my code to have the `instanceof` operator, nothing has changed with my output. – SIHB007 Feb 20 '15 at 16:09
  • What output are you expecting? Give a concrete condition? Also, your Rank and Suit class `equals()` methods may be incorrectly implemented as well. Would need to check those. – Kon Feb 20 '15 at 16:12
  • I have used three PlayingCards in the tester, two of those are actually identical (test and test3). I am expecting different outputs from `test.equals(test2)` and `test.equals(test3)`. In both cases I just get `"No Match, True"` when I am expecting one of them to have `"Match"`. Does that help? – SIHB007 Feb 20 '15 at 16:16
  • @SIHB007 Your enums do not have an `equals()` implementation. – Kon Feb 20 '15 at 16:32
  • I have been specificly told for the assignement to write an equals() class specifically for this, what do I need to do to actually implement equals()? – SIHB007 Feb 20 '15 at 16:33
  • @SIHB007 This would be a good time to get very familiar with this: http://stackoverflow.com/questions/27581/what-issues-should-be-considered-when-overriding-equals-and-hashcode-in-java – Kon Feb 20 '15 at 16:37
  • Ok I'm probably grasping as straws here.. but I need a hashcode method or something? I will be adding the equals() method in the link and see if I can get it to work. Is there anything major in particular to find in the link other than the selected answer? – SIHB007 Feb 20 '15 at 16:48
  • @SIHB007 Let me simplify. Your next goal should be to implement a concrete, by-the-book, and rock solid `equals()` method. You should generally implement `hashCode()` as well. To do this, you'll need to do a little reading to understand best practices. I read mine in a book a long while back. You may want to google around for "Proper equals implementation in Java` to see what is helpful for you, or you can ask another question on SO if something is not clear :) – Kon Feb 20 '15 at 16:52
  • I will ask another question about equals and hashcode later on, and try and track down equals implementation then. – SIHB007 Feb 20 '15 at 17:08
  • @SIHB007 Ok, good luck. If you're done with this question you can either mark my answer as resolved or hold out for a more detailed response. I'll see if I see your later question today. – Kon Feb 20 '15 at 17:18
  • @SIHB007 although it is true that you should override hashCode(), that is unrelated to your immediate problem. Your original implementation was fine; your tester was misleading you. See my answer. – NamshubWriter Feb 20 '15 at 17:26
1

Your original equals method was implemented correctly. The outputs are the same because the equals method prints the same result regardless of what suit.equals(other.suit) && rank == other.rank evaluates to.

Edit: You could see that with a small change to the end of your equals() method:

PlayingCard other = (PlayingCard) otherObject;
boolean result = suit.equals(other.suit) && rank == other.rank;
System.out.println("No Match, " + result);
return result;

I recommend removing the print statements, and testing your code with a test framework like JUnit.

You should, of course, override hashCode() as well.

NamshubWriter
  • 23,549
  • 2
  • 41
  • 59
-2

You can create your comparator for that type of objects. It will be something like this:

    public class ComparatorCard<T extends PlayingCard> implements Comparator<T> {

    @Override
    public int compare(T objectOne, T ObjectTwo) { 

        return objectOne.toString().compareTo(ObjectTwo.toString()); // Change what you need where
    }
}

With this class you can also save the PalyingCards by an order in some Java structure that use priorities.