📜  JS++ |亚型多态性

📅  最后修改于: 2022-05-13 01:55:51.214000             🧑  作者: Mango

JS++ |亚型多态性

子类型描述类型关系,子类型多态性使得为超类型定义的操作可以安全地替换为子类型。具体来说,想象一下“Cat”类和“Animal”类之间的关系。 (记住:类在 JS++ 中创建数据类型。)在这种情况下,在类型关系的上下文中,“Cat”是“Animal”的子类型,“Animal”是“Cat”的超类型。简单来说,“猫”“是”“动物”,但“动物”不是“猫”。理论上,这意味着适用于“Animal”数据类型的所有操作都应该接受对“Cat”类型数据的操作;但是,反之则不然:为数据类型“Cat”定义的操作将无法安全地对“Animal”类型的数据进行操作。

如果你还记得上一节的代码,猫和狗是有名字的驯养动物。然而,并不是所有的动物都应该被命名,所以我们的“Animal”类没有使用名称参数。因此,虽然我们可以在 'Cat' 的实例上定义并调用 'name' getter,但我们不能安全地将 'Cat' 实例替换为 'Animal' 实例。在 JS++ 中,如果你尝试这样做,你会得到一个错误。

子类型多态性允许我们以更抽象的方式编写代码。例如,在原始类型的上下文中,“字节”表示 0 到 255 范围内的数字。同时,“整数”表示更大范围内的数字:-2、147、483、648 到 2、147 , 483, 647. 因此,我们可以替换类型为 'byte' 的数字,其中需要 'int' 类型的数字:

int add(int a, int b) {
    return a + b;
}

byte a = 1;
byte b = 1;
add(a, b);

因此,我们能够更“一般地”表达算法和函数,因为我们可以为任何给定的算法或函数接受更广泛的数据(按数据类型分类)。

重要的是不要混淆子类型和继承,即使这些概念在面向对象编程中密切相关。子类型描述类型关系,而继承与实现有关(例如用派生类扩展基类的实现)。子类型可以应用于接口,而“继承”不能。当我们介绍接口时,这个概念将在后面的部分中变得更加清晰。

作为实际理解子类型的起点,我们可以更改 main.jspp 以便我们所有变量的数据类型变为“动物”,但我们将类的实例化保留为子类型:

import Animals;

Animal cat1 = new Cat("Kitty");
cat1.render();
Animal cat2 = new Cat("Kat");
cat2.render();
Animal dog = new Dog("Fido");
dog.render();
Animal panda = new Panda();
panda.render();
Animal rhino = new Rhino();
rhino.render();

编译你的代码。它应该可以成功编译,因为在实例化期间,所有属于“Animal”子类型的数据都可以分配给“Animal”类型的变量。在这种情况下,“Cat”、“Dog”、“Panda”和“Rhino”数据都可以安全地分配给“Animal”变量。

但是,有一个小“陷阱”。打开 index.html。您将再次看到所有动物的渲染,但是,如果您将鼠标悬停在任何动物图标上,您将看不到任何名称!要理解为什么会发生这种情况,我们必须了解静态与动态多态性,这将在下一节中解释。 (简而言之,我们实际上通过在 'render' 方法上使用 'overwrite' 关键字来指定我们想要“静态”或编译时多态性。因此,我们得到了指定的行为。)