从构造函数调用可覆盖方法的问题
the issues of calling overridable methods from constructor
如果可以请帮我把这句话说清楚
这里作者说:
Do not call overridable methods from constructors. When creating a
subclass object, this could lead to an overridden method being called
before the subclass object is fully initialized. Recall that when you
construct a subclass object, its constructor first calls one of the
direct superclass’s constructors. If the superclass constructor
calls an overridable method, the subclass’s version of that method
will be called by the superclass constructor—before the subclass
constructor’s body has a chance to execute.
我无法理解如何在超类的构造函数中调用子类版本的可重写方法
TnX
演示将调用子方法的示例:
class Foo {
static class Parent {
Parent() {
someMethod();
}
void someMethod() {}
}
static class Child extends Parent {
@Override void someMethod() {
throw new AssertionError("Invoked");
}
}
public static void main(String[] args) {
new Child(); // Throws Exception.
}
}
输出:
Exception in thread "main" java.lang.AssertionError: Invoked
at Foo$Child.someMethod(Foo.java:16)
at Foo$Parent.<init>(Foo.java:9)
at Foo$Child.<init>(Foo.java:14)
at Foo.main(Foo.java:21)
为了说明使用某些(简单的)代码这是一个坏主意的原因,请考虑以下两个 classes:
class Greeter {
protected Greeter() {
printHello();
}
protected void printHello() {
System.out.println("Hello");
}
}
看起来很简单,只要你实例化它就会打印 Hello
。现在让我们扩展它:
class NamedGreeter extends Greeter {
private String name;
public NamedGreeter(String name) {
this.name = name;
}
@Override
protected void printHello() {
System.out.println("Hello " + name);
}
}
意图显然是让 NamedGreeter
在实例化时通过名字问候你,但实际上它总是打印 Hello null
因为当 NamedGreeter
时超级构造函数首先被调用被实例化。
由于多态性的工作方式,任何时候 printHello()
方法在 NamedGreeter
上被调用(即使该调用是从 Greeter
class 中调用的) NamedGreeter
中的实现将被调用。从父 class 的构造函数中调用该方法意味着即使子 class 扩展它,也不会初始化子定义的任何字段,仅仅是因为不可能在子中做任何事情调用父构造函数之前的构造函数(如初始化字段)。
你首先要区分实例化和初始化。实例化是创建类型实例的过程(为其分配 space 并获取对该 space 的引用)。初始化是将实例的状态设置为其初始值的过程。
采用以下类型层次结构:
class Foo {
public Foo() {}
}
class Bar extends Foo {
public Bar() {super();}
}
新实例创建表达式
new Bar();
导致实例化和初始化。实例化首先发生。 Java 创建具体类型的实例 Bar
.
然后需要进行初始化。在继承层次结构中,初始化遵循相同的层次结构,但从上到下。
Object
|
Foo
|
Bar
Object
的构造函数首先运行以初始化定义为 Object
的一部分的状态,然后 Foo
的构造函数是 运行 初始化状态被定义为 Foo
的一部分,最后 Bar
的构造函数是 运行 初始化 Bar
中定义的状态。 您的实例仍然是 Bar
. 类型,因此多态性仍然适用。如果您调用一个实例方法,并且该方法在层次结构的较低位置被重写,则将调用该实现。
这就是那句话所指的。这很危险。在这里阅读更多内容:
What's wrong with overridable method calls in constructors?
如果可以请帮我把这句话说清楚
这里作者说:
Do not call overridable methods from constructors. When creating a subclass object, this could lead to an overridden method being called before the subclass object is fully initialized. Recall that when you construct a subclass object, its constructor first calls one of the direct superclass’s constructors. If the superclass constructor calls an overridable method, the subclass’s version of that method will be called by the superclass constructor—before the subclass constructor’s body has a chance to execute.
我无法理解如何在超类的构造函数中调用子类版本的可重写方法
TnX
演示将调用子方法的示例:
class Foo {
static class Parent {
Parent() {
someMethod();
}
void someMethod() {}
}
static class Child extends Parent {
@Override void someMethod() {
throw new AssertionError("Invoked");
}
}
public static void main(String[] args) {
new Child(); // Throws Exception.
}
}
输出:
Exception in thread "main" java.lang.AssertionError: Invoked
at Foo$Child.someMethod(Foo.java:16)
at Foo$Parent.<init>(Foo.java:9)
at Foo$Child.<init>(Foo.java:14)
at Foo.main(Foo.java:21)
为了说明使用某些(简单的)代码这是一个坏主意的原因,请考虑以下两个 classes:
class Greeter {
protected Greeter() {
printHello();
}
protected void printHello() {
System.out.println("Hello");
}
}
看起来很简单,只要你实例化它就会打印 Hello
。现在让我们扩展它:
class NamedGreeter extends Greeter {
private String name;
public NamedGreeter(String name) {
this.name = name;
}
@Override
protected void printHello() {
System.out.println("Hello " + name);
}
}
意图显然是让 NamedGreeter
在实例化时通过名字问候你,但实际上它总是打印 Hello null
因为当 NamedGreeter
时超级构造函数首先被调用被实例化。
由于多态性的工作方式,任何时候 printHello()
方法在 NamedGreeter
上被调用(即使该调用是从 Greeter
class 中调用的) NamedGreeter
中的实现将被调用。从父 class 的构造函数中调用该方法意味着即使子 class 扩展它,也不会初始化子定义的任何字段,仅仅是因为不可能在子中做任何事情调用父构造函数之前的构造函数(如初始化字段)。
你首先要区分实例化和初始化。实例化是创建类型实例的过程(为其分配 space 并获取对该 space 的引用)。初始化是将实例的状态设置为其初始值的过程。
采用以下类型层次结构:
class Foo {
public Foo() {}
}
class Bar extends Foo {
public Bar() {super();}
}
新实例创建表达式
new Bar();
导致实例化和初始化。实例化首先发生。 Java 创建具体类型的实例 Bar
.
然后需要进行初始化。在继承层次结构中,初始化遵循相同的层次结构,但从上到下。
Object
|
Foo
|
Bar
Object
的构造函数首先运行以初始化定义为 Object
的一部分的状态,然后 Foo
的构造函数是 运行 初始化状态被定义为 Foo
的一部分,最后 Bar
的构造函数是 运行 初始化 Bar
中定义的状态。 您的实例仍然是 Bar
. 类型,因此多态性仍然适用。如果您调用一个实例方法,并且该方法在层次结构的较低位置被重写,则将调用该实现。
这就是那句话所指的。这很危险。在这里阅读更多内容:
What's wrong with overridable method calls in constructors?