构建器内部的多形性要领的行为
构建器挪用的分级布局(顺序)为我们带来了一个有趣的问题,可能说让我们进入了一种骑虎难下的排场。若当前位于一个构建器的内部,同时挪用筹备构建的谁人工具的一个动态绑定要领,那么会呈现什么环境呢?在原始的要领内部,我们完全可以想象会产生什么——动态绑定的挪用会在运行期间举办理会,因为工具不知道它到底从属于要领地址的谁人类,照旧从属于从它衍生出来的某些类。为保持一致性,各人也许会认为这应该在构建器内部产生。
但实际环境并非完全如此。若挪用构建器内部一个动态绑定的要领,会利用谁人要领被包围的界说。然而,发生的结果大概并不如我们所愿,并且大概造成一些难于发明的措施错误。
从观念上讲,构建器的职责是让工具实际进入存在状态。在任何构建器内部,整个工具大概只是获得部门组织——我们只知道基本类工具已获得初始化,但却不知道哪些类已经担任。然而,一个动态绑定的要领挪用却会在分级布局里“向前”可能“向外”前进。它挪用位于衍生类里的一个要领。假如在构建器内部做这件工作,那么对付挪用的要领,它要哄骗的成员大概尚未获得正确的初始化——这显然不是我们所但愿的。
通过调查下面这个例子,这个问题便会昭然若揭:
//: PolyConstructors.java // Constructors and polymorphism // don't produce what you might expect. abstract class Glyph { abstract void draw(); Glyph() { System.out.println("Glyph() before draw()"); draw(); System.out.println("Glyph() after draw()"); } } class RoundGlyph extends Glyph { int radius = 1; RoundGlyph(int r) { radius = r; System.out.println( "RoundGlyph.RoundGlyph(), radius = " + radius); } void draw() { System.out.println( "RoundGlyph.draw(), radius = " + radius); } } public class PolyConstructors { public static void main(String[] args) { new RoundGlyph(5); } } ///:~
在Glyph中,draw()要领是“抽象的”(abstract),所以它可以被其他要领包围。事实上,我们在RoundGlyph中不得差池其举办包围。但Glyph构建器会挪用这个要领,并且挪用会在RoundGlyph.draw()中止,这看起来好像是有意的。但请看看输出功效:
Glyph() before draw() RoundGlyph.draw(), radius = 0 Glyph() after draw() RoundGlyph.RoundGlyph(), radius = 5
当Glyph的构建器挪用draw()时,radius的值甚至不是默认的初始值1,而是0。这大概是由于一个点号可能屏幕上基础什么都没有画而造成的。这样就不得不开始查找措施中的错误,试着找出措施不能事情的原因。
前一节报告的初始化顺序并不十分完整,而那是办理问题的要害地址。初始化的实际进程是这样的:
(1) 在采纳其他任何操纵之前,为工具分派的存储空间初始化成二进制零。
(2) 就象前面论述的那样,挪用基本类构建器。此时,被包围的draw()要了解获得挪用(简直是在RoundGlyph构建器挪用之前),此时会发明radius的值为0,这是由于步调(1)造成的。
(3) 凭据原先声明的顺序挪用成员初始化代码。
(4) 挪用衍生类构建器的主体。
采纳这些操纵要求有一个前提,那就是所有对象都至少要初始化成零(可能某些非凡数据范例与“零”等价的值),而不是仅仅留作垃圾。个中包罗通过“合成”技能嵌入一个类内部的工具句柄。假如假使健忘初始化谁人句柄,就会在运行期间呈现违例事件。其他所有对象城市酿成零,这在观当作果时凡是是一个严重的告诫信号。
在另一方面,应对这个措施的功效提高鉴戒。从逻辑的角度说,我们好像已举办了无懈可击的设计,所以它的错误行为令人很是不行思议。并且没有从编译器哪里收到任何报错信息(C++在这种环境下会表示出更公道的行为)。象这样的错误会很等闲地被人忽略,并且要花很长的时间才气找出。
因此,设计构建器时一个出格有效的法则是:用尽大概简朴的要领使工具进入停当状态;假如大概,制止挪用任何要领。在构建器内独一可以或许安详挪用的是在基本类中具有final属性的那些要领(也合用于private要领,它们自动具有final属性)。这些要领不能被包围,所以不会呈现上述潜在的问题。