Java equals when equality can't be determined

equals, java

Solution

You could add this condition in the equals method:

if(this.isFaceDown || other.isFaceDown) return false;

I think it's the only way to completely hide the card if it's faced down. The problem is that when adding it to a set you can have duplicates if the cards you're adding are faced down.

Problem

I'm wondering what's the proper Java programming paradigm for overriding the `equals` (and `hashCode`) methods of an object of class C in cases where either (a) there's not enough information to determine if two instances of C are equal or (b) the calling method should not be able to determine if two instance of C are equal. In my project, for example, I have a `PlayingCard` class. It seems to me that if a `PlayingCard` is face up, then calling methods should have access to its properties, but if it's face down, then those properties should remain unknown: ``` class PlayingCard { private Rank rank; private Suit suit; private boolean isFaceDown; public PlayingCard(Rank rank, Suit suit, boolean isFaceDown) { this.rank = rank; this.suit = suit; this.isFaceDown = isFaceDown; } public Rank getRank() { return isFaceDown ? null : rank; } public Suit getSuit() { return isFaceDown ? null : suit; } ``` It also seems like, for the sake of the Java Collections Framework, two playing cards should be equal if they have the same rank and suit: ``` public boolean equals(Object obj) { // attempt #1 if(this == obj) return true; if(obj == null) return false; if(!(obj instanceof PlayingCard)) return false; PlayingCard other = (PlayingCard) obj; if(rank != other.rank) return false; if(suit != other.suit) return false; return true; } } ``` But that reveals too much information: ``` class Malicious { public Rank determineRankOfFaceDownCard(PlayingCard faceDownCard) { Set<PlayingCard> allCards = /* a set of all 52 PlayingCards face up */; for(PlayingCard c : allCards) { if(c.equals(faceDownCard)) { return c.getRank(); } } return null; } } ``` Using the `getRank` and getSuit` methods doesn't seem to work either: ``` public boolean equals(Object obj) { // attempt #1 if(this == obj) return true; if(obj == null) return false; if(!(obj instanceof PlayingCard)) return false; PlayingCard other = (PlayingCard) obj; if(getRank() != other.getRank()) return false; if(getSuit() != other.getSuit()) return false; return true; } } /* outside the PlayingCard class */ Set<PlayingCard> s = new HashSet<PlayingCard>(); s.add(new PlayingCard(Rank.ACE, Suit.SPADES, true)); s.contains(new PlayingCard(Rank.TWO, Rank.HEARTS, true)); // returns true ``` How have other developers dealt with this situation? Is this a situation where throwing some sort of `RuntimeException` would be appropriate? Thanks for any input and ideas.

Original source