每个不可变的类都应该是最终的吗?
我正在设计一个用于二十一点游戏的 Card 类。
我的设计是创建一个带有 getValue() 的 Card 类,该类返回 J 11、Q 12 和 K 13,然后使用 BlackjackCard 类扩展它以覆盖该方法,以便这些卡返回 10。
然后我突然想到:Card 类的对象应该是不可变的。所以我重新阅读了Effective Java 2nd Edition,看看该怎么做,我发现不可变类需要是final的,以避免子类破坏不可变性。
我也在网上查了一下,大家似乎都同意这一点。
那么 Card 类应该是最终的吗?
如何打破此类的不变性,扩展它:
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();
}
}
谢谢。
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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
子类实际上无法修改其父类中的
private final
属性的值,但它可以表现,就好像它已经修改一样,这就是 Effective Java 警告: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:答案是肯定的,卡必须是最终的。
结合 K.Claszen 和 lwburk 的响应,请参阅以下内容:
此扩展完全忽略父状态,并将其替换为自己的可变状态。现在,在多态上下文中使用 Card 的类不能依赖它的不可变性。
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.
您可以这样做:
}
扩展类甚至不必声明与父类相同的构造函数。它可以有一个默认的构造函数,并且不关心父类的最终成员。 [该陈述是错误的 - 请参阅评论]。
You can do this:
}
The extending class does even not have to declare the same constructor as the parent class. It can have a default constructor and does not care anything about the final members of the parent class. [That statement is wrong - see the comments].
当他们说不可变类应该是最终的时,他们指的是如何确保不变性,而不是因为某些东西是不可变的,所以它必须是最终的。这是一个很小的区别。如果您不希望延长课程时间,则应以最终决定为准。
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.
总的来说,这是一个很好的建议。但是,如果您控制所有代码,那么有时能够扩展不可变类(可能使用附加信息创建另一个不可变类)会很有用。与大多数建议一样,您必须做出明智的选择,确定它们何时有意义、何时无效。
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.
如果任意代码可以扩展不可变类,那么任意代码都可以生成行为与不可变类非常相似的对象,但不是不可变的。如果编写这种代码的人除了自己之外无法损坏任何东西,那么这种情况可能是可以容忍的。如果有人可以使用这样的类来绕过安全措施,那么就不应该容忍。
请注意,有时拥有一个可扩展的类会很有用,但它代表所有派生类保证不变性。当然,这样的类及其使用者必须依赖该类的继承者不要做任何奇怪的事情,但有时这种方法可能仍然比任何替代方法更好。例如,一个人可能有一个类,该类应该在某个特定时间执行某些操作,但该类的对象可以任意别名。如果该类、其任何字段、其任何派生类中的任何字段等都不能使用派生类型,则这样的类不会非常有用,因为类的定义将限制可以执行的操作类型执行。最好的方法可能是不将该类声明为最终类,但在文档中明确指出可变子类的行为将不一致或不可预测。
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.
您可以有一个与游戏绑定的
Valuator
并从类中删除getValue
,这样Card
就可以是final:
评估器的工作方式如下:
现在,如果您仍然想保留
Card
层次结构,请标记Card
的方法final
将阻止子类覆盖它们。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.