在Java中从构造函数调用抽象方法可以吗?
假设我有一个实现可运行接口的抽象基类在Java中从构造函数调用抽象方法可以吗?,java,methods,constructor,virtual,abstract,Java,Methods,Constructor,Virtual,Abstract,假设我有一个实现可运行接口的抽象基类 public abstract class Base implements Runnable { protected int param; public Base(final int param) { System.out.println("Base constructor"); this.param = param; // I'm using this param here new Thread(
public abstract class Base implements Runnable {
protected int param;
public Base(final int param) {
System.out.println("Base constructor");
this.param = param;
// I'm using this param here
new Thread(this).start();
System.out.println("Derivative thread created with param " + param);
}
@Override
abstract public void run();
}
这是几个衍生类中的一个
public class Derivative extends Base {
public Derivative(final int param) {
super(param);
}
@Override
public void run() {
System.out.println("Derivative is running with param " + param);
}
public static void main(String[] args) {
Derivative thread = new Derivative(1);
}
}
关键是我希望我的基类做一些常规的事情,而不是每次都复制它。
实际上,它运行良好,输出总是相同的:
基本构造函数
使用参数1创建的派生线程
导数正在使用参数1运行
但在JAVA中,启动一个线程调用构造函数中的抽象方法是否安全?因为C++和C语言在大多数情况下都是不安全的,据我所知。
谢谢大家! 从构造函数调用抽象方法是一种非常糟糕的做法。从构造函数调用的方法应始终是私有的或最终的,以防止重写
查看指向问题的链接不是一个好主意,因为调用run()时,派生对象可能尚未初始化。如果run()依赖于派生中的任何状态,它可能会失败 在您的简单案例中,它是有效的。但是子类没有意义。你可以简单地
public Base(final int param, Runnable action) {
new Thread(action).start();
这段代码演示了为什么不应从构造函数调用抽象方法或任何其他可重写方法:
abstract class Super {
Super() {
doSubStuff();
}
abstract void doSubStuff();
}
class Sub extends Super {
String s = "Hello world";
void doSubStuff() {
System.out.println(s);
}
}
public static void main(String[] args) {
new Sub();
}
运行时,将打印null
。这意味着构造函数中唯一的“安全”方法是私有和/或最终方法
另一方面,您的代码实际上并没有从构造函数调用抽象方法。相反,您将一个未初始化的对象传递给另一个线程进行处理,这更糟糕,因为您正在启动的线程可能会被赋予优先级,并在
基
完成初始化之前执行。将此
从构造函数中传递出去称为“让此
从构造函数中转义”,并且可能导致一些特别讨厌和奇怪的bug,因为对象可能处于不一致的状态
当
This
被传递到另一个线程时尤其如此,如本例所示。由于JVM有权对线程中的语句进行重新排序,因此可能会出现未定义的行为/状态。让构造函数调用抽象或虚拟方法(其契约指定可以从构造函数调用)是否有任何不当之处,从这样的上下文调用该方法必须是安全的,并且只能调用其他类似安全的方法?@supercat:我想这取决于您对文档的信任程度,您对其他人遵循此类文档的信任程度,以及您对可能扩展此类类或其任何子类以适当传播或记住引用此类警告的每个人的信任程度。在我看来,这是相当多的信任。我更喜欢那些可以通过自动化测试来证明的东西,但这还不够公平。我想到的最大的使用案例是,在这种情况下,派生类将重写一个方法以返回一个常量,该常量对于每个子类的所有实例都必须相同,并且在构造函数和其他地方都需要,因此典型的实现是int getWoozleForType(){return 23;}
。将这样一个东西传递给构造函数并将其存储在实例字段中似乎有点棘手,我想不出任何其他方法似乎也有吸引力。我不明白这是如何失败的?我觉得很好。显然你是对的,但我无法理解继承抽象方法和定义它的功能如何打印null
?@finrayment问题是,当Super
构造函数(在构建Sub
之前调用)试图调用dosubstanff()
,字符串s
尚未初始化。