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.
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.
I am not sure if I am missing something, but maybe there should be some logic outside of the class holding your comparator that will not compare cards when not visible?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With