我有一个类,它定义了给定屏幕的所有基本参数。从这里开始,应用程序中的每个屏幕都是它的子类。我需要每个屏幕(即子类)在其实现中设置变量的值(即,每个屏幕必须定义它在导航树中的级别)。
此外,理想情况下,这个变量应该是final
在子类中设置的时候(我意识到这可能是不可能的)。
解决此问题的最佳方法是什么?有没有办法在 Java 中正确执行这种类型的行为?
我有一个类,它定义了给定屏幕的所有基本参数。从这里开始,应用程序中的每个屏幕都是它的子类。我需要每个屏幕(即子类)在其实现中设置变量的值(即,每个屏幕必须定义它在导航树中的级别)。
此外,理想情况下,这个变量应该是final
在子类中设置的时候(我意识到这可能是不可能的)。
解决此问题的最佳方法是什么?有没有办法在 Java 中正确执行这种类型的行为?
@pst 的评论导致了这个解决方案。
这不能用变量来完成。但是抽象类可以要求实现一个特定的方法:这个方法可以返回适用的值
从声明abstract
函数到设置或返回变量,您可以强制任何子类正确实现它。
接下来,必须由外部类的每个子类调用该函数。这意味着它必须在外部类的某个地方完成。这可以在外部类的无参数构造函数中完成,而不必担心子类调用super
:
注意:如果构造函数没有显式调用超类构造函数,Java 编译器会自动插入对超类的无参数构造函数的调用。如果超类没有无参数构造函数,则会出现编译时错误。Object 确实有这样的构造函数,所以如果 Object 是唯一的超类,则没有问题。 (Java 文档:超级)
基于此,此解决方案将保持并正确强制设置变量,只要:
编码:
超类:
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
}
}
与其强制子类实例初始化字段,不如遵循组合策略,让父类构造函数采用实现接口的参数,该接口提供您希望初始化的字段。
class Screen {
final Configuration config;
Screen(Configuration config) {
this.config = config;
}
// or
Screen(ConfigFactory configFactory) {
this.config = configFactory.make();
}
}
interface ConfigFactory {
Configuration make();
}
我会告诫不要要求子类实例初始化配置,比如使用抽象方法实现。父类构造函数中的赋值发生在子类实例初始化之前,隐式地使配置的正确计算成为静态。
如果计算不是静态的,您可能会冒空引用或 NullPointerExceptions 被跟随您的开发人员(或者您自己,如果您的内存不够完美)。让你的合作者(和你自己)更容易,并使约束明确。
正如@Ketan 在@BT 的回答中提到的那样,从构造函数调用可覆盖的方法并不是特别好的做法(https://help.semmle.com/wiki/display/JAVA/Non-final+method+invocation+in +构造函数)
避免此问题的一种方法是为该字段设置一个抽象的(受保护的)吸气剂。因此超类不再具有该字段,但仍然可以在超类中使用 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;
}
}