如何在Java中强制子类设置变量?

27
我有一个类,定义了给定屏幕的所有基本参数。从这里开始,应用程序中的每个屏幕都是此类的子类。我需要每个屏幕(即子类)在其实现中设置变量的值(即,每个屏幕必须定义其在导航树中的级别)。
此外,理想情况下,当子类设置变量时,该变量应该是final(我意识到这可能不可能)。
如何最好地解决这个问题?是否有一种正确强制执行此类行为的Java方法?
3个回答

40

@pst的评论引导了这个解决方案。

这不能用一个变量实现。但是,一个抽象类可以要求实现特定的方法:这个方法可以返回适用的值。

通过声明一个抽象函数来设置或返回变量,你可以强制任何子类正确地实现它。

接下来,每个外部类的子类都必须调用该函数。这意味着它必须在外部类的某个地方完成。这可以在外部类的无参构造函数中完成,而不必担心子类调用super:

注意:如果一个构造函数没有显式地调用超类的构造函数,Java编译器会自动插入对超类的无参数构造函数的调用。如果超类没有无参数构造函数,则会得到一个编译时错误。Object具有这样的构造函数,因此如果Object是唯一的超类,就没有问题。 (Java文档:Super)

基于此,只要满足以下条件之一,这个解决方案就能保持并正确地强制设置变量:

  1. 在超类中没有创建其他构造函数(因此在子类中不能使用super调用不同的构造函数)
  2. 超类中的所有其他构造函数仍然在内部调用默认构造函数

代码:

超类:

public abstract class SuperClass {
    // Variable all inner classes must set
    static int myVar = 0;

    public SuperClass() {
        myVar = giveValue();
    }

    public abstract int giveValue();
}

子类:

public class SubClass extends SuperClass {

    @Override
    public int giveValue() {
        return 5; // individual value for the subclass
    }

}

1
谢谢!救了我的一天!! - DevAndroid
1
从构造函数中调用可重写方法!这会引发像这样的问题吗?https://help.semmle.com/wiki/display/JAVA/Non-final+method+invocation+in+constructor - Ketan
1
@Ketan的链接已经失效。 - BabyishTank
@Ketan的链接现在可以在这里找到:https://codeql.github.com/codeql-query-help/java/java-non-final-call-in-constructor/ - undefined

13
与其强制要求子类实例化字段,您可以采用组合策略,让父类构造函数接受一个参数,该参数实现了提供您希望初始化的字段的接口。
class Screen {
  final Configuration config;
  Screen(Configuration config) {
    this.config = config;
  }
  // or
  Screen(ConfigFactory configFactory) {
    this.config = configFactory.make();
  }
}

interface ConfigFactory {
  Configuration make();
}

我建议不要要求子类实例化配置,例如使用抽象方法实现。父类构造函数中的赋值发生在子类实例化之前,隐式地使得配置的计算是静态的。
如果计算不是静态的,你就会冒着开发者跟随你(或者自己因为记忆不够好)出现空指针引用或NullPointerException的风险。让你的合作者(和你自己)更轻松一些,明确约束条件。

你能否在你的回答中添加类似于接受答案示例中的类比呢?这对我来说有点模糊。 - Line

3

正如@Ketan在@B T的答案中提到的那样,从构造函数中调用可重写方法并不是一种特别好的做法。 (https://help.semmle.com/wiki/display/JAVA/Non-final+method+invocation+in+constructor)

避免这个问题的一种方法是为字段设置一个抽象(protected)getter。因此,超类不再具有该字段,但可以使用getter在超类中访问它。每个子类都被强制声明该字段,因为它必须覆盖抽象getter。

超类:

public abstract class SuperClass {

  public SuperClass() {}

  protected abstract int getMyVar();

  public void functionUsingMyVar(){
     int a = 12 + getMyVar();
  }
}

子类1:

public class SubClass1 extends SuperClass {

  private int myVar;

  public SubClass1() {
     super();
     myVar = 1;
  }

  @Override
  protected int getMyVar(){
     return myVar;
  }

}

子类2:

 public class SubClass2 extends SuperClass {

  private int myVar;

  public SubClass2() {
     super();
     myVar = 1;
  }

  @Override
  protected int getMyVar(){
     return myVar;
  }

}

与其在超类中拥有giveValue()方法(该方法是可重写的并在构造函数中调用):

public abstract class SuperClass {

  private int myVar;

  public SuperClass() {
     myVar = giveValue();
  }

  protected abstract int giveValue();

  public void functionUsingMyVar(){
     int a = 12 + myVar;
  }
}

这段代码存在错误,例如类没有继承SuperClass。 - powder366

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接