Class 如何处理类内的多态性
在具有动态类型的语言中,多态性的使用可能会触发超类上的错误 我将尝试用一个简单的例子来解释我的问题: 假设一种具有动态类型的语言(如ECMAScript)和以下类结构:Class 如何处理类内的多态性,class,polymorphism,dynamic-typing,Class,Polymorphism,Dynamic Typing,在具有动态类型的语言中,多态性的使用可能会触发超类上的错误 我将尝试用一个简单的例子来解释我的问题: 假设一种具有动态类型的语言(如ECMAScript)和以下类结构: class A{ private idA; public A(){ idA=0; } public foo(){ update(); if (this.idA!=3) Throws new Exception(" What is happening?
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;
}
}
在这个非常简单的示例中,当我创建类B的对象时,B::foo()调用父级A::foo(),后者调用“update”。该对象是B的一个实例,因此称为“更新”的函数是B::update,之后,在B::foo中,更新函数再次被称为(B::update)。最终的结果是永远不会调用::update,而idA仍然是0
单独使用时,类A可以正常工作,但使用B扩展后,函数foo()失败
此问题的正确解决方案是什么:
1) 强制类A调用A::update,这意味着每次调用自己的函数都会产生一段难看的代码(保护超级类):
2) update是A::update的扩展,因此B::update必须调用自己的父函数(准备子类并处理问题):
但是在本例中是调用update的A::foo,而不是B::foo。这意味着其他问题
3) 任何其他解决方案
作为总结:
如何保护超类代码不受多态性影响?
- 在超级类中添加保护
- 创建子类时处理这些问题
- 语言必须做到这一点!(不知道是否可以使用动态类型语言)
编辑:从构造函数中找出问题并澄清一些要点。通常认为从构造函数中调用实例方法,尤其是
虚拟实例方法是一种非常糟糕的做法,正是因为这个原因(但也因为对象没有完成“初始化”)然而)
3) 任何其他解决方案
医生,我这样做很痛
那就别那么做
说真的,如果您需要在A
的构造函数中设置IdA
,请不要通过调用update
来实现,而要通过在A
的构造函数中显式设置IdA
的值来实现,您可能不会喜欢我的答案,但是:约定和纪律
为……订立公约
- 如果子类可以安全地重写方法而不调用父类实现
- 当子类必须调用重写方法的父类实现时
- 当子类不能重写父类方法时
记录这些约定并坚持它们。它们可能是代码的一部分;以注释或命名约定的形式(任何适合您的方式)。我可以想到这样的事情:
/*
* @final
*/
function shouldNotBeOverridden() {
}
/*
* @overridable
* @call-super
*/
function canBeOverriddenButShouldBeCalledFromChildClasses() {
}
/*
* @overridable
*/
function canBeOverridenWithoutBeingCalledFromChildClasses() {
}
class A{
private idA;
public A(){
idA=0;
}
public foo(){
update();
if (this.idA!=3) Throws new Exception("idA not set by update");
}
protected update(){
this.idA = 3;
}
}
class B extends A{
private idB;
public B(){
super();
idB=0;
}
@Override
public foo(){
super.foo();
// Any operation between A::update and B::update()
if (this.idB!=5) Throws new Exception("idB not set by super.foo");
}
@Override
protected update(){
super.Update()
this.idB = 5;
}
}
这可能有助于阅读您的代码的人了解他可以覆盖或不覆盖哪些方法
如果有人仍然覆盖您的@final
方法,您希望能够进行彻底的测试;)
我想问一个关于python的类似问题:
您可以在其中添加一条评论,大意如下:
# We'll fire you if you override this method.
如果语言允许一个类以这种方式调用另一个类的私有方法,那么程序员必须理解并接受它。如果我理解您的目标,那么应该覆盖foo和update,并保护update。然后,必要时,他们会在父类中调用该方法。派生的foo方法不需要调用update,因为在父类中调用foo可以解决这个问题。代码的工作原理如下:
/*
* @final
*/
function shouldNotBeOverridden() {
}
/*
* @overridable
* @call-super
*/
function canBeOverriddenButShouldBeCalledFromChildClasses() {
}
/*
* @overridable
*/
function canBeOverridenWithoutBeingCalledFromChildClasses() {
}
class A{
private idA;
public A(){
idA=0;
}
public foo(){
update();
if (this.idA!=3) Throws new Exception("idA not set by update");
}
protected update(){
this.idA = 3;
}
}
class B extends A{
private idB;
public B(){
super();
idB=0;
}
@Override
public foo(){
super.foo();
// Any operation between A::update and B::update()
if (this.idB!=5) Throws new Exception("idB not set by super.foo");
}
@Override
protected update(){
super.Update()
this.idB = 5;
}
}
我更改了异常以符合预期。基类应该保护自己不受有害重写的影响。为了保持一致,它应该是开放的扩展,但关闭修改。重写update
是对基类预期行为的有害修改。在您的示例中,重写update
没有任何好处,因为A::update
和B::update
都是处理私有变量的私有方法。根据B::foo
中的异常判断,甚至不期望它们一起执行。如果B::update
的名称不同,那么您的实现就不会有任何问题。无论如何,这可能没问题:因为我所知道的任何语言都不允许重写私有方法,B::update
可以隐藏a::update
,而不是重写它
根据语言的不同,您可以限制可以以不同方式重写哪些方法。有些语言需要一个指示符(通常是关键字或属性)来表示一个方法可以被重写,而另一些语言则表示它不能被重写。私有方法通常是不可重写的,但并非所有语言都有访问修饰符,而且所有内容实际上都是公共的。在这种情况下,您必须使用@PoByBolek建议的某种约定
tl;dr:孩子们与父母的私事无关。谢谢你的回答,但问题仍然存在:应该是保护自己不受多态性影响的父类、子类还是编程语言?我将对问题进行编辑,以避免这种误解。(不管怎么说,你的答案是+1)当你编辑你的问题时,如果你的问题实质上改变了对它的理解,最好是问一个新问题。我理解你的观点,但问题仍然是一样的。正如我在这个问题的第一个版本中所问,你的解决方案既不是规范的,也不是理论的。你刚刚回答了明显的2%的问题。此外,我使用了一个简单的例子来询问多态性,您回答了一个与多态性无关的构造函数问题。阿德里安·梅尔:嗯,好吧(我不同意你的意见,只是c