Adr*_*ire 6 polymorphism class dynamic-typing
在具有动态类型的语言中,使用多态可能会触发超类上的错误.
我将尝试用一个简单的例子解释我的问题:假设一个动态类型的语言(如ECMAScript)和以下类结构:

class A{
private idA;
public A(){
idA=0;
}
public foo(){
update();
if (this.idA!=3) Throws new Exception(" What is happening? ");
}
private update(){
this.idA = 3;
}
}
class B extends A{
private idB;
public B(){
super();
idB=0;
}
public foo(){
super.foo();
// Any operation between A::update and B::update()
if (this.idB!=0) Throws new Exception("hmmm, that could not happend!");
update();
}
private update(){
this.idB = 5;
}
}
Run Code Online (Sandbox Code Playgroud)
在这个非常简单的例子中,当我创建类B的对象时,B :: foo()调用父A :: foo(),它调用"update".该对象是B的实例,因此调用的"更新"函数是B :: update,之后,在B :: foo中,再次调用更新函数(B :: update).最终的结果是永远不会调用A :: update,而idA仍为0.
单独使用时,A类正常工作,但在用B扩展后,函数foo()失败.
这个问题的正确解决方案是什么:
1)强制A类调用A :: update,这意味着每次调用自己的函数时都会看到一个丑陋的代码(保护超类):
A::foo(){
A::update();
if (this.idA!=3) Throws new Exception(" What is happening? ");
}
Run Code Online (Sandbox Code Playgroud)
2)B :: update是A :: update的扩展,所以B :: update必须将自己称为父函数(准备子类,并处理问题):
B::foo(){
super.foo();
... // Any operation that must be performed between A::update and B::update
}
B::update(){
super.update();
this.idB = 5;
}
Run Code Online (Sandbox Code Playgroud)
但在这种情况下是调用更新的A :: foo,而不是B :: foo.这意味着其他问题.
3)任何其他解决方案.
作为总结:
如何保护超类代码不受多态影响?
我正在寻找这个问题的理论/规范解决方案.
编辑:从构造函数中解决问题并澄清一些观点.
通常认为调用实例方法是非常糟糕的做法,特别是virtual在构造函数中的实例方法完全是出于这个原因(但也是因为对象尚未完成"初始化").
3)任何其他解决方案.
医生,我这样做会很痛.
那就不要那样做!
说真的,如果你需要IdA在构造函数中设置A,不要通过调用update来做,通过显式设置IdA构造函数中的值来实现A.
基类应该保护自己免受有害的覆盖。为了与开放/封闭原则保持一致,它应该对扩展开放,但对修改封闭。覆盖update是对基类预期行为的有害修改。在您的示例中,重写没有任何好处,update因为A::update和B::update都是处理私有变量的私有方法。根据 中的异常判断,甚至不期望它们应该一起执行B::foo。如果B::update命名不同,您的实现不会有任何问题。无论如何,这可能没问题:因为我所知道的任何语言都不会让你覆盖私有方法,所以B::update可以隐藏A::update而不是覆盖它。
根据语言的不同,您可以以不同的方式限制哪些方法可以被覆盖。有些语言需要一个指示符(通常是关键字或属性)来表明方法可以被重写,而其他语言则需要表明它不能。私有方法通常不可重写,但并非所有语言都具有访问修饰符,并且所有内容实际上都是公共的。在这种情况下,您必须使用 @PoByBolek 建议的某种约定。
tl;dr:孩子与父母的私处没有关系。