正如我们在上一节中提到的,如果我们想要运行时多态性,使用强制转换可能会导致不干净的代码。
举个例子,让我们改变我们的主要观点。jspp代码使我们所有的动物都在一个数组中。从那里,我们将循环数组以渲染动物。打开主管道。jspp并将代码更改为:
import Animals; Animal[] animals = [ new Cat("Kitty"), new Cat("Kat"), new Dog("Fido"), new Panda(), new Rhino() ]; foreach(Animal animal in animals) { if (animal instanceof Cat) { ((Cat) animal).render(); } else if (animal instanceof Dog) { ((Dog) animal).render(); } else { animal.render(); } }
现在,我们的代码甚至没有最初的代码那么优雅,它只是实例化了动物,指定了最具体的类型,并称为render()。
然而,这段代码可以大大简化,直到变得优雅。事实上,我们可以将“foreach”循环缩减为一条语句。答案是:虚拟方法。
虚拟方法支持“后期绑定”换句话说,要调用的特定方法是在运行时而不是编译时解析的。我们不需要所有的“instanceof”检查、所有的强制转换,以及我们在上面代码中看到的所有“if”语句。我们可以做一些更优雅的事情。
首先,开放动物。jspp并更改“render”方法以包含“virtual”修饰符:
external $; module Animals { class Animal { protected var $element; protected Animal(string iconClassName) { string elementHTML = makeElementHTML(iconClassName); $element = $(elementHTML); } public virtual void render() { $("#content").append($element); } private string makeElementHTML(string iconClassName) { string result = '<div class="animal">'; result += '<i class="icofont ' + iconClassName + '"></i>'; result += "</div>"; return result; } } }
拯救动物。jspp。这是我们唯一需要做的改变。
然而,仅仅将我们的方法虚拟化是不够的。在猫身上。jspp和狗。jspp,我们在他们的“渲染”方法中使用“覆盖”修饰符。“overwrite”修饰符指定编译时分辨率。我们需要运行时解析。我们所要做的就是换一只猫。jspp和狗。jspp使用“override”修饰符而不是“overwrite”修饰符。为了简洁起见,我将只显示对Cat的更改。jspp但是你需要改成狗。jspp也是:
external $; module Animals { class Cat : Animal { string _name; Cat(string name) { super("icofont-animal-cat"); _name = name; } override void render() { $element.attr("title", _name); super.render(); } } }
就这样。我们所要做的就是改变修饰语。现在我们终于可以编辑main了。所以只有 一句话 在循环内部:
import Animals; Animal[] animals = [ new Cat("Kitty"), new Cat("Kat"), new Dog("Fido"), new Panda(), new Rhino() ]; foreach(Animal animal in animals) { animal.render(); }
编译代码并打开索引。html。一切都应该顺利。现在我们已经能够大规模地简化代码,并且仍然能够获得预期的行为。具体来说,我们将“foreach”循环的代码从以下内容缩减为:
foreach(Animal animal in animals) { if (animal instanceof Cat) { ((Cat) animal).render(); } else if (animal instanceof Dog) { ((Dog) animal).render(); } else { animal.render(); } }
为此:
foreach(Animal animal in animals) { animal.render(); }
我们之所以能够如此显著地简化代码,是因为将方法标记为“虚拟”意味着潜在的运行时多态性。与“override”修饰符一起,编译器知道我们希望对“render”方法进行后期绑定,因此“late”绑定正好在需要时发生:当且仅当需要解析“render”方法时(在“foreach”循环内),才会在运行时解析“render”方法。