java 如何强制子类在java中设置变量?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15015771/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to force subclasses to set a variable in java?
提问by B T
I have a class which defines all of the basic parameters for a given screen. From here every screen in the application is a subclass of this. I need everyscreen (i.e. subclass) to set the value of a variable in its implementation (namely, each screen must define what level it is in a navigation tree).
我有一个定义给定屏幕的所有基本参数的类。从这里开始,应用程序中的每个屏幕都是 this 的子类。我需要每个屏幕(即子类)在其实现中设置变量的值(即,每个屏幕必须定义它在导航树中的级别)。
Also, ideally, this variable should be final
when it is set in the sub classes (I realise this probably isn't possible).
此外,理想情况下,这个变量应该final
是在子类中设置时(我意识到这可能是不可能的)。
What is the best way to go about this? Is there a way to correctly enforce this type of behaviour in Java?
解决这个问题的最佳方法是什么?有没有办法在 Java 中正确地强制执行这种类型的行为?
回答by B T
@pst's comment lead to this solution.
@pst 的评论导致了这个解决方案。
This can't be done with a variable. But an abstract class can require that a particular method is implemented: this method could return the applicable value
这不能用变量来完成。但是抽象类可以要求实现一个特定的方法:这个方法可以返回适用的值
From declaring an abstract
function to set or return the variable, you can force any subclass to implement it correctly.
从声明一个abstract
函数来设置或返回变量,您可以强制任何子类正确实现它。
Next, the function must be called by every single subclass of the outer class. This implies that it must be done somewhere in the outer class. This can be done in the no-argument constructor of the outer class without having to worry about subclasses calling super
:
接下来,该函数必须被外部类的每个子类调用。这意味着它必须在外部类的某个地方完成。这可以在外部类的无参数构造函数中完成,而不必担心子类调用super
:
Note: If a constructor does not explicitly invoke a superclass constructor, the Java compiler automatically inserts a call to the no-argument constructor of the superclass. If the super class does not have a no-argument constructor, you will get a compile-time error. Object does have such a constructor, so if Object is the only superclass, there is no problem. (Java docs: Super)
注意:如果构造函数没有显式调用超类构造函数,Java 编译器会自动插入对超类的无参数构造函数的调用。如果超类没有无参数构造函数,您将收到编译时错误。Object 确实有这样的构造函数,所以如果 Object 是唯一的超类,就没有问题。 (Java 文档:超级)
Based on that, this solution will hold up and correctly forcethe variable to be set as long as either:
基于此,该解决方案将支持并正确强制设置变量,只要满足以下任一条件:
- No other constructor is created in the superclass (hence super can't be used in a subclass to call a different constructor instead)
- All other constructors in the superclass still call the default constructor internally
- 没有在超类中创建其他构造函数(因此不能在子类中使用 super 来调用不同的构造函数)
- 超类中的所有其他构造函数仍然在内部调用默认构造函数
The code:
代码:
Superclass:
超类:
public abstract class SuperClass {
// Variable all inner classes must set
static int myVar = 0;
public SuperClass() {
myVar = giveValue();
}
public abstract int giveValue();
}
Subclass:
子类:
public class SubClass extends SuperClass {
@Override
public int giveValue() {
return 5; // individual value for the subclass
}
}
回答by jlb
Rather than enforce that child class instances initialize the fields, you could follow a strategy of composition by having your parent class constructor take a parameter implementing an interface that provides the fields you wish to have initialized.
不是强制子类实例初始化字段,您可以遵循组合策略,让父类构造函数接受一个参数,实现一个接口,该接口提供您希望初始化的字段。
class Screen {
final Configuration config;
Screen(Configuration config) {
this.config = config;
}
// or
Screen(ConfigFactory configFactory) {
this.config = configFactory.make();
}
}
interface ConfigFactory {
Configuration make();
}
I would caution against requiring a subclass instance initializing the configuration, say using an abstract method implementation. The assignment in the parent class constructor occurs before the subclass instance is initialized, implicitlymaking proper computation of the configuration static.
我会警告不要要求子类实例初始化配置,比如使用抽象方法实现。父类构造函数中的赋值发生在子类实例初始化之前,隐式地使配置的正确计算静态化。
If the computation isn't static, you risk null references or NullPointerExceptions by developers following you (or yourself if your memory is less than perfect). Make it easier on your collaborators (and yourself) and make the constraints explicit.
如果计算不是静态的,那么跟随您的开发人员(或者如果您的记忆不够完美,您自己)可能会面临空引用或 NullPointerExceptions 的风险。让您的合作者(和您自己)更轻松,并明确限制。
回答by Lulu
As mentioned by @Ketan in @B T's answer, invoking an overridable method from constructor is not especially a good practice (https://help.semmle.com/wiki/display/JAVA/Non-final+method+invocation+in+constructor)
正如@Ketan 在@BT 的回答中提到的,从构造函数调用一个可覆盖的方法并不是一个特别好的做法(https://help.semmle.com/wiki/display/JAVA/Non-final+method+invocation+in+constructor)
One way to avoid this problem consists in having an abstract (protected) getter for the field. Hence the superclass doesn't have the field anymore, but it is still accessible in the super class using the getter. Each subclass is forced to declare the field because it must override the abstract getter.
避免此问题的一种方法是为该字段设置一个抽象的(受保护的)getter。因此,超类不再具有该字段,但仍然可以使用 getter 在超类中访问它。每个子类都被迫声明该字段,因为它必须覆盖抽象 getter。
Superclass:
超类:
public abstract class SuperClass {
public SuperClass() {}
protected abstract int getMyVar();
public void functionUsingMyVar(){
int a = 12 + getMyVar();
}
}
Subclass1:
子类1:
public class SubClass1 extends SuperClass {
private int myVar;
public SubClass1() {
super();
myVar = 1;
}
@Override
protected int getMyVar(){
return myVar;
}
}
Subclass2:
子类2:
public class SubClass2 extends SuperClass {
private int myVar;
public SubClass2() {
super();
myVar = 1;
}
@Override
protected int getMyVar(){
return myVar;
}
}
instead of having for the superclass (where giveValue() is overridable and called in the constructor) :
而不是拥有超类(其中 giveValue() 是可覆盖的并在构造函数中调用):
public abstract class SuperClass {
private int myVar;
public SuperClass() {
myVar = giveValue();
}
protected abstract int giveValue();
public void functionUsingMyVar(){
int a = 12 + myVar;
}
}