I was designing a Card class to be used in a Blackjack game.
My design was to make a Card class with a getValue() that returns, for example, 11 for J, 12 for Q and 13 for K, and then extend it with a BlackjackCard class to override that method so that those cards return 10.
Then something hit me: objects of the Card class should be immutable. So I re-read Effective Java 2nd Edition to see what to do and I there I found that immutable classes need to be final, to avoid a subclass to break the immutability.
I also looked in Internet and everyone seems to agree in that point.
So should the Card class be final?
How can you break the immutability of this class, be extending it:
class Card {
private final Rank rank;
private final Suit suit;
public Card(Rank rank, Suit suit) {
this.rank = rank;
this.suit = suit;
}
public Rank getRank() {
return rank;
}
public Suit getSuit() {
return suit;
}
public int getValue() {
return rank.getValue();
}
}
Thanks.
A subclass cannot actually modify the values of
private final
properties in its parent, but it could behave as though it has, which is what Effective Java warns against:You could have a
Valuator
that's tied to the game and remove thegetValue
from the class, this wayCard
can befinal
:And valuators work like this:
Now, if you still want to keep your
Card
hierarchy, marking theCard
's methodsfinal
will prevent a child class from overriding them.When they say immutable classes should be final they are referring to how you can ensure immutability not that because something is immutable it has to be final. Its a minor distinction. If you don't want your class ever extended it should be final.
If arbitrary code can extend an immutable class, then arbitrary code can produce objects that behave a lot like the immutable class, but aren't immutable. If people who write such code would be unable to damage anything but themselves, then such a situation may be tolerable. If someone could use such a class to bypass security measures, then it should not be tolerated.
Note that it can at times be useful to have a class which is extensible, but which promises immutability on behalf of all derived classes. Such a class and its consumers would, of course, have to rely upon inheritors of the class not to do anything weird and wacky, but sometimes such an approach may still be better than any alternative. For example, one might have a class which is supposed to perform some action at some particular time, with the proviso that objects of the class may be arbitrarily aliased. Such a class would not be terribly useful if neither the class, nor any of its fields, nor any fields in any of its derived classes, etc. could use derived types, since the definition of a class would restrict what types of actions could be performed. The best approach would probably be to have the class not be declared final, but make very clear in the documentation that the behavior of mutable subclasses would not be consistent or predictable.
In general, this is a good recommendation. however, if you control all the code, then sometimes it is useful to be able to extend an immutable class (possibly to create another immutable class with additional info). as with most recommendations, you have to make intelligent choices as to when they make sense and when they may not.
Answer is yes, Card needs to be final.
Combining the responses of K. Claszen and lwburk, see the following:
This extension completely ignores the parent state and replaces it with its own, mutable state. Now classes that use Card in polymorphic contexts can't depend upon its being immutable.