JS++ 静态多态与动态多态
静态多态是在编译时发生的多态,而动态多态是在运行时(应用程序执行期间)发生的多态。
静态多态性的一个方面是早期绑定。在早期绑定中,要调用的具体方法在编译时就已解决。(JS++也支持通过虚拟函数的后期绑定,我们将在后面介绍)。早期绑定更快,因为没有运行时的开销。早期绑定是JS++的默认行为,也是最常见的。如果你想要晚期绑定,你必须明确指定它。同样,我们将在后面的章节中介绍晚期绑定。
在我们的代码中,我们为’猫’和’狗’类的render()方法指定了’覆盖’修改器。这使得方法隐藏成为可能,但它指定了早期绑定。下面是Cat.jspp的代码。
overwrite void render() {
$element.attr("title", _name);
super.render();
}
后来,当我们在main.jspp中把猫的类型改为 “动物 “时,我们的猫的类型也从 “猫 “改为 “动物”。你可能会说,”是的,但我们还是用’猫’类来实例化我们的猫对象”。虽然这是真的,但这也意味着我们的猫变量现在可以接受任何符合 “动物 “约束的类型的数据(包括 “动物 “本身,如果我们没有指定 “动物 “的保护构造函数)。例如,现在这样的代码就完全可以接受了。
import System;
import Animals;
Animal cat1 = new Cat("Kitty");
if (Math.random(1, 10) > 3) {
cat1 = new Dog("Fido");
}
cat1.render();
忽略 “System “的导入,但它导入了Math.random()函数。它是用来说明这个例子的。我还特意将我们的变量名称保留为’cat1’,以说明这一点。cat1’的类型是’动物’。因此,所有’动物’类型的对象都可以分配给’cat1’(包括’狗’类型的对象)。
如果你实际编译、运行上面的代码,并充分刷新,你会发现你的 “猫 “有时会被渲染成狗。
正如你所观察到的,当我们有一个 “动物 “类型时,数据在我们的程序中可以是 “猫”,也可以基于运行时的随机数生成而随机变成 “狗”。随机数不是在编译时生成的。随机数是在应用程序执行期间(运行时)生成的。因此,从编译器的角度来看,如果我们要解析’render’方法,我们要把它解析为用户指定的类型’Animal’的’render’方法,因为这是最正确的。回顾一下,在子类型中,所有的猫和狗都是动物,但不是所有的动物都是猫和狗。
因此,这应该可以帮助你理解早期绑定和静态/编译时多态性。
除此以外。作为规范的数据类型
数据类型也是一种规范。我们正在指定什么是正确的程序。例如,你不会指定一个 “减法 “函数接受两个 “字符串 “类型的参数。在我们的例子中,如果我们想要一只猫,我们应该指定 “猫 “的类型,而不是泛指 “动物”。虽然用更一般的术语来表达算法的能力是可取的,但它不一定是正确的。
从技术上讲,这个程序仍然是类型正确的。在两种情况下(’猫’和’狗’),我们都有一个类型为’动物’的对象。只是我们的’动物’被命名为’cat1’,所以上面的代码的一个潜在的修正可能是将变量重命名为’animal’或类似的东西来表达我们的意图。
另一个可能的解决方法是,如果我们想让’cat1’永远是一只猫,就把数据类型限制为’猫’而不是’动物’。如果你这样做,你会得到一个编译错误,因为’狗’不是’猫’的一个子类型。
JSPPE5000: Cannot convert `Animals.Dog' to `Animals.Cat' at line 6 char 8 at main.jspp
在运行时多态性中,类型是在运行时确定的。例如,我们可以使用’instanceof’操作符来检查运行时的数据类型。改变你的main.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.");
}
继续刷新,你应该看到有时我们有一个 “猫 “实例,有时我们有一个 “狗 “实例。然而,这些类型(以及由此产生的信息)是在运行时确定的–而不是在编译时。