静态多态性是在编译时发生的多态性,而动态多态性是在运行时(应用程序执行期间)发生的多态性。
静态多态性的一个方面是早期绑定。在早期绑定中,要调用的特定方法在编译时解析。(JS++还支持通过虚拟函数进行后期绑定,我们将在后面介绍。)早期绑定速度更快,因为没有运行时开销。早期绑定是JS++的默认行为,也是最常见的行为。如果想要后期绑定,必须显式指定它。同样,我们将在后面的部分讨论后期绑定。
在代码中,我们为“Cat”和“Dog”类render()方法指定了“overwrite”修饰符。这启用了隐藏方法,但它指定 早期装订 .这是猫的密码。jspp:
overwrite void render() { $element.attr("title", _name); super.render(); }
后来,我们把猫的种类改成了“动物”。jspp,我们的猫从“猫”变成了“动物”。你可能会说,“是的,但我们还是用‘cat’类实例化了cat对象。”虽然这是真的,但这也意味着我们的cat变量现在可以接受满足“动物”约束的任何类型的数据(包括“动物”本身,如果我们没有在“动物”上指定受保护的构造函数)。例如,这现在成为完全可以接受的代码:
import System; import Animals; Animal cat1 = new Cat("Kitty"); if (Math.random(1, 10) > 3) { cat1 = new Dog("Fido"); } cat1.render();
忽略“系统”导入,但它会导入数学。random()函数。这是用来说明这个例子的。我还特意将变量的名称保留为“cat1”,以说明这一点。”“cat1”的类型为“Animal”。因此,所有“动物”类型的对象都可以指定为“cat1”(包括“狗”类型的对象)。
如果你真的编译、运行上面的代码并充分刷新,你会注意到你的“猫”有时会被渲染成狗。
正如你所观察到的,当我们有一个“动物”类型时,我们的程序中的数据可以是“猫”,也可以根据运行时的随机数生成随机成为“狗”。随机数不是在编译时生成的。随机数是在应用程序执行(运行时)期间生成的。因此,从编译器的角度来看,如果我们要解析“render”方法,我们要将其解析为用户指定类型“Animal”的“render”方法,因为它是 最正确 .回想一下,在子类型中,所有猫和狗都是动物,但并非所有动物都是猫和狗。
因此,这有望帮助您理解早期绑定和静态/编译时多态性。
旁白:数据类型作为规范
数据类型也是规范。我们是 指定 什么构成 正确的程序 。例如,您不会指定一个’subtract’函数来接受两个’string’类型的参数。在我们的例子中,如果我们想要一只猫,我们应该指定“猫”类型,而不是泛化为“动物”。虽然用更一般的术语表达算法的能力是可取的,但它可能并不总是正确的。
从技术上讲,该项目仍在进行中 输入正确 .在这两种情况下(“猫”和“狗”),我们都有一个“动物”类型的对象。碰巧我们的“Animal”被命名为“cat1”,所以上面代码的一个潜在修复方法可能是将变量重命名为“Animal”或类似的东西来表达我们的意图。
如果我们希望“cat1”始终是一只猫,另一个可能的解决方案是将数据类型限制为“cat”而不是“Animal”。如果这样做,您将得到一个编译错误,因为“Dog”不是“Cat”的子类型:
JSPPE5000: Cannot convert `Animals.Dog' to `Animals.Cat' at line 6 char 8 at main.jspp
在运行时多态性中,类型是在运行时确定的。例如,我们可以使用’instanceof’操作符来检查运行时数据类型。改变你的主要想法。jspp文件并观察结果:
import System; import Animals; external $; Animal animal = new Cat("Kitty"); if (Math.random(1, 10) > 3) { animal = new Dog("Fido"); } if (animal instanceof Cat) { $("#content").text("We have a CAT."); } else { $("#content").text("We have a DOG."); }
继续刷新,你应该看到,有时我们有一个“猫”的例子,有时我们有一个“狗”的例子。但是,类型(以及生成的消息)是在运行时确定的,而不是在编译时确定的。