Java 每个不变的类都应该是最终的吗?

Java 每个不变的类都应该是最终的吗?,java,immutability,final,Java,Immutability,Final,我正在设计一个用于21点游戏的卡片类 我的设计是使用getValue()创建一个Card类,该类返回11表示J,12表示Q,13表示K,然后使用BlackjackCard类对其进行扩展以覆盖该方法,从而使这些卡返回10 然后我想到了一件事:Card类的对象应该是不可变的。所以我重读了《有效的Java第二版》,看看该怎么做,我发现不可变类必须是最终类,以避免子类破坏不可变性 我也在网上查了一下,大家似乎都同意这一点 那么卡片类应该是最终的吗 如何打破这个类的不变性,扩展它: class Card

我正在设计一个用于21点游戏的卡片类

我的设计是使用getValue()创建一个Card类,该类返回11表示J,12表示Q,13表示K,然后使用BlackjackCard类对其进行扩展以覆盖该方法,从而使这些卡返回10

然后我想到了一件事:Card类的对象应该是不可变的。所以我重读了《有效的Java第二版》,看看该怎么做,我发现不可变类必须是最终类,以避免子类破坏不可变性

我也在网上查了一下,大家似乎都同意这一点

那么卡片类应该是最终的吗

如何打破这个类的不变性,扩展它:

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();
  }
}

谢谢。

当他们说不可变类应该是最终类时,他们指的是如何确保不可变性,而不是因为某些东西是不可变的,所以必须是最终类。这只是一个小区别。如果你不想让你的课程扩展,它应该是最终的。

你可以这样做:

class MyCard extends Card {

  public MyCard(Rank rank, Suit suit) {
    super(rank, suit);
  }

  @Override
  public Rank getRank() {
    // return whatever Rank you want
    return null;
  }

  @Override
  public Suit getSuit() {
    // return whatever Suit you want
    return null;
  }

  @Override
  public int getValue() {
    // return whatever value you want
    return 4711;
  }
}


扩展类甚至不必声明与父类相同的构造函数。它可以有一个默认构造函数,并且不关心父类的最终成员。[该语句是错误的-请参阅注释]。

子类实际上无法修改其父类中的
private final
属性的值,但它的行为可能就好像它已经修改了一样,这就是:

确保该类不能扩展。 这可以防止粗心或恶意 子类以避免破坏 类的不可变行为 表现得好像对象的状态 变了


答案是肯定的,卡片必须是最终的

结合K.Claszen和lwburk的回答,见以下内容:

public class MyCard extends Card {
    private Rank myRank;
    private Suit mySuit;

    public MyCard(Rank rank, Suit suit) {
        this.myRank = rank;
        this.mySuit = suit;
    }

    @Override public Rank getRank() { return myRank; }

    public void setRank(Rank rank) { this.myRank = rank; }

    @Override public Suit getSuit() { return mySuit; }

    public void setSuit(Suit suit) { this.mySuit = suit; }

    @Override public int getValue() { return myRank.getValue(); }
}

此扩展完全忽略父状态,并将其替换为自己的可变状态。现在,在多态上下文中使用Card的类不能依赖于它是不可变的。

一般来说,这是一个很好的建议。但是,如果您控制所有代码,那么有时候能够扩展一个不可变类(可能创建另一个具有附加信息的不可变类)是很有用的。与大多数建议一样,您必须明智地选择它们什么时候有意义,什么时候可能没有意义。

如果任意代码可以扩展不可变类,那么任意代码可以生成行为与不可变类非常相似但并非不可变的对象。如果编写这种代码的人除了自己之外无法损坏任何东西,那么这种情况可能是可以容忍的。如果有人可以使用这样一个类来绕过安全措施,那么它就不应该被容忍


请注意,拥有一个可扩展的类有时是有用的,但它保证代表所有派生类的不变性。当然,这样一个类及其使用者必须依赖于该类的继承者,才能不做任何奇怪和古怪的事情,但有时这种方法可能仍然比其他方法更好。例如,可能有一个类,该类应该在某个特定时间执行某些操作,但有一个限制条件,即该类的对象可以任意使用别名。如果该类、其任何字段或其任何派生类中的任何字段等都不能使用派生类型,则此类将不会非常有用,因为类的定义将限制可以执行的操作类型。最好的方法可能是不将类声明为final,但在文档中明确说明可变子类的行为不一致或不可预测。

您可以有一个绑定到游戏的
评估器
,并从类中删除
getValue
,这样,
可以是
最终的

final 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;
  }
}
评估人员的工作方式如下:

interface CardValuator {
  int getValue(Card card);
}

class StandardValuator implements CardValuator {
  @Override
  public int getValue(Card card) {
    return card.getRank().getValue();
  }
}

class BlackjackValuator implements CardValuator {
  @Override
  public int getValue(Card card) {
    ...
  }
}

现在,如果您仍然希望保留
层次结构,则标记
的方法
final
将防止子类重写它们。

您的
MyCard
类将不会编译。它无法访问
排名
套装
。最后一条语句不正确。必须设置任何最终成员变量。另外,您的示例无法编译,因为您无法重置
this.rank
this.suit
。但是你的其他观点仍然是合法的。@lwburk&@jtahlborn你是对的-无法重置最终成员-请键入此快速+1相关的一点是类或其方法不能扩展,声明类final只是实现这一点的几种方法之一。