正如我们在子类型多态性一章中了解到的,为超类型定义的操作可以安全地用其子类型替换。因此,为“动物”对象定义的操作可以安全地接受“猫”或“狗”对象。
此外,我们提到,您不应该混淆子类型和继承。子类型描述类型之间的关系,而继承则与实现有关。这种区别在接口中变得很明显。
接口与我们刚刚了解的抽象类和方法类似,只是接口的所有“方法”都是抽象的。接口不包含任何实现。
接口指定类型,但不指定实现。让我们做一个交通类比。你需要从旧金山到洛杉矶。你可能不在乎如何到达那里,你只需要一种交通方式。接口不定义“方式”,因此在本例中,接口将是“运输方式”“如何”将在“火车”、“汽车”、“飞机”或“公共汽车”等课程中实施。只要“交通方式”可以移动,就应该满足你的需求。然而,你不能乘坐抽象的“交通方式”你需要一辆“汽车”、“火车”、“飞机”或“公共汽车”。
要可视化这些关系:
interface IModeOfTransport { void move(); } class Car : IModeOfTransport { override void move() { // ... } } class Train : IModeOfTransport { override void move() { // ... } } class Airplane : IModeOfTransport { override void move() { // ... } } class Bus : IModeOfTransport { override void move() { // ... } }
每个具体类的动作都不同。例如,“汽车”的“移动”方法的实现与“飞机”截然不同,因为飞机可以飞行。
从这些关系中,您应该能够理解您可以实例化上面的一个类,例如“Car”,如下所示:
Car rentalCar = new Car();
假设你正在开发一个旅游网站。你想给用户提供从旧金山到洛杉矶的几种选择。在这种情况下,您可能希望概括该类型:
IModeOfTransport transport = new Car();
现在,当用户选择一个不同的选项(而不是“Car”)时,您可以轻松地将“transport”变量中保存的数据类型更改为“Train”、“Plane”或“Bus”的实例。
然而,你也认识到你不能通过“交通方式”从旧金山到洛杉矶。你必须指定哪种交通方式。因此,您应该认识到这将导致编译器错误:
IModeOfTransport transport = new IModeOfTransport();
因此,我们只使用接口来指定类型和类型关系。接口不提供实现,因此无法实例化。
有了这种理解,我们就能改变我们的“动物”型关系。首先,让我们从一个非常基本的例子开始。我们的一些动物没有颜色。现在只是一个黑白网页。让我们加点颜色。
导航到OOP项目中包含动物的“src/Animals/”文件夹。杰斯普,猫。杰普,狗。jspp等。在这个文件夹中,创建一个名为IColorizable的新文件。jspp。里面可以用肖像画。jspp,让我们输入以下代码:
module Animals { interface IColorizable { void colorize(); } }
您会注意到的第一件事是,我们在接口名称前面加了“I”(大写字母“I”)。这被称为 命名约定 .命名约定帮助我们命名类、接口、模块、函数和变量,这些名称将与加入我们团队的其他程序员、其他第三方库等使用的名称一致。“I”是接口中的“I”,前缀为“it+”。
注意,我们不需要“抽象”修饰符。接口内的所有方法签名都被认为是抽象的,因为接口内不允许实现。
接下来,我们必须指定哪些类与IColorizable共享关系。现在,让我们只给“狗”类一些颜色:
import Externals.DOM; external $; module Animals { class Dog : Animal, IColorizable { string _name; Dog(string name) { super("icofont-animal-dog"); _name = name; } final void render() { $element.attr("title", _name); super.render(); } final void talk() { alert("Woof!"); } final void colorize() { $element.css("color", "brown"); } } }
现在,我们必须在“Dog”实例上调用colorize()方法。编辑主页。jspp:
import Animals; external $; IColorizable fido = new Dog("Fido"); fido.colorize(); Animal[] animals = [ new Cat("Kitty"), new Cat("Kat"), fido, new Panda(), new Rhino() ]; foreach(Animal animal in animals) { animal.render(); } $("#content").append("<p>Number of animals: " + Animal.getCount().toString() + "</p>");
试着编译。您将得到一个错误:
[ ERROR ] JSPPE5034: Could not determine type for array literal. All elements in array literal must be the same, a mixture of primitive types, or descendants of the same base class at line 8 char 19 at main.jspp
发生这种情况的原因是因为’fido’变量的类型为’IColorizable’。同时,数组只接受“Animal”类型的元素。如果您还记得,我们的“Dog”类直接继承自“IColorizable”,而“Animal”基类则没有继承。因此,我们不能将“IColorizable”的对象直接插入到“Animal”类型的数组中;否则,我们将能够执行JavaScript中的不安全操作。
请注意,在图表中,“Animal”和“IColorizable”之间没有类型关系。
有多种方法可以解决这个问题。我们将通过使所有的动物都可以着色来解决这个问题。编辑狗。jspp并删除与“IColorizable”的类型关系。但是,将方法实现保留为“colorize”。稍后你会明白原因的。这里是一个可视化的东西,你必须删除在狗。jspp:
import Externals.DOM; external $; module Animals { class Dog : Animal, IColorizable{ string _name; Dog(string name) { super("icofont-animal-dog"); _name = name; } final void render() { $element.attr("title", _name); super.render(); } final void talk() { alert("Woof!"); } final void colorize() { $element.css("color", "brown"); } } }
现在打开动物。jspp并添加:
external $; module Animals { abstract class Animal : IColorizable { protected var $element; private static unsigned int count = 0; protected Animal(string iconClassName) { string elementHTML = makeElementHTML(iconClassName); $element = $(elementHTML); attachEvents(); Animal.count++; } public static unsigned int getCount() { return Animal.count; } public virtual void render() { $("#content").append($element); } public abstract void colorize(); public abstract void talk(); private void attachEvents() { $element.click(talk); } private string makeElementHTML(string iconClassName) { string result = '<div class="animal">'; result += '<i class="icofont ' + iconClassName + '"></i>'; result += "</div>"; return result; } } }
因为我们的“动物”类是“抽象的”,所以我们不需要“实现”这个“着色”方法。相反,我们只是将“colorize”方法声明为“abstract”,从而将其进一步推迟到“Animal”的派生类。还记得我们没有从“Dog”类中删除“colorize”方法吗?这就是为什么。我们已经将实现“colorize”的责任委托给了“Animal”的派生类,但我们仍然能够描述“Animal”和“IColorizable”之间的类型关系,其中“IColorizable”是“Animal”的超类型。
现在,我们只需要给其他动物添加颜色。我会保持简短。下面是一个模板,用于说明需要添加到“Cat”中的代码类型。jspp,熊猫。jspp和Rhino。jspp’:
final void colorize() { $element.css("color", colorName); }
将“colorName”替换为要制作动物的颜色。让你的猫“金”,熊猫“黑”,犀牛“银”。
编辑主页。jspp:
import Animals; external $; Animal[] animals = [ new Cat("Kitty"), new Cat("Kat"), new Dog("Fido"), new Panda(), new Rhino() ]; foreach(Animal animal in animals) { animal.colorize(); animal.render(); } $("#content").append("<p>Number of animals: " + Animal.getCount().toString() + "</p>");
编译:
$ js++ src/ -o build/app.jspp.js
开放索引。html。结果应该是这样的: