面向对象写久了,很多人会形成一种很自然的直觉:me/this指向的就是正在创建的那个对象;既然对象最终会是子类实例,那在父类构造器里调用一个实例方法时,应该会自动走到子类的重定义实现。这个直觉在不同语言里,结果可能完全不一样,甚至同一门语言也会因为初始化顺序而让你得到看似调用成功、实则被覆盖回去的诡异现象。
这类问题的危险之处不在于它难,而在于它太像理所当然:代码没有语法错误,逻辑读起来也顺,测试却悄悄跑偏。更麻烦的是,一旦它出现在框架类、基类、或者多人协作的公共组件里,影响面会被继承链放大。
下面用 ABAP、Java、JavaScript 各举一个最小可复现例子,把构造器、重定义、动态分派、成员初始化顺序之间的关系一次讲透,并给出在 SAP 项目里更稳妥的设计方式。示例思路与结论在业界也很常见,尤其是关于构造器不要调用可重写方法的规则,在安全规范里甚至被单独强调过。(Stack Overflow)
现象从 ABAP 开始:父类构造器里调用方法,子类重定义却完全不生效
先看 A