这是我参与11月更文挑战的第20天,活动详情查看:2021最后一次更文挑战
类实现接口
前面我们学习过 TS 中的接口,有两种作用:
- 面向对象编程——行为的抽象
- 描述对象的形状
现在我们就来学习接口和类之间的关系。
首先最基本的就是类实现接口。
那么接口是什么?接口就是将一些相似的类的共同的属性和方法抽象出来,比如下面的例子:
class Bird {
fly() {
console.log('鸟在飞');
}
jump() {
console.log('鸟在跳');
}
}
class Bee {
fly() {
console.log('蜜蜂在飞');
}
honey() {
console.log('蜜蜂在采蜜');
}
}
Bird 这个类,有 fly 和 jump 这两个方法;Bee 这个类,有 fly 和 honey 这两个方法,我们可以把公有的方法抽象成一个接口。我们使用 interface 来定义一个接口 Wings,这个接口定义了一个方法 fly。
接下来,我们使用 implements 来将鸟和蜜蜂,都来实现这个翅膀接口。 这样的好处,就是我们可以在统一的地方进行约束这个接口的类型,然后如果将来有其他的类,需要满足这种接口的话,可以直接实现它,这样就可以约束实现这个接口的类,都拥有了统一的行为。
interface Wings {
fly(): void;
}
class Bird implements Wings {
fly() {
console.log('鸟在飞');
}
jump() {
console.log('鸟在跳');
}
}
class Bee implements Wings {
fly() {
console.log('蜜蜂在飞');
}
honey() {
console.log('蜜蜂在采蜜');
}
}
需要注意的是,你的类可以先继承另一个类,然后再实现接口,并且可以同时实现多个接口。
我们来看下面的例子:
interface Wings {
fly(): void;
}
interface Mouth {
sing(): void;
}
abstract class Animal {
abstract eat(): void;
}
class Bird extends Animal implements Wings, Mouth {
fly() {
console.log('鸟在飞');
}
eat() {
console.log('eating');
}
sing() {
console.log('singing');
}
}
这里的接口和抽象类的用法,比较类似,都是定义了一些公共的方法,需要具体的类去实现。
他们的区别在于:
接口就像一个插件一样,是用来增强类的;而抽象类,是具体类的抽象概念,比如这里的Bird 就是动物的一种。
另一个区别是,类实现接口,是一个多对多的关系,一个类可以实现多个接口,一个接口,也可以被多个类实现。
而类继承类,是一个一对多的关系,一个类的父类,只能有一个,而一个类的子类,可以有多个。
习题:根据已声明的接口,下面用法正确的是?
interface animalBehavior {
getFood(): string;
}
interface animalInfo {
setAge(age: number): void;
}
// A
class Animal extends animalInfo {
private age = 10;
setAge(age: number) {
this.age = age;
}
}
// B
class Animal implements animalInfo, animalBehavior {
private age = 10;
setAge(age: number) {
this.age = age;
}
}
// C
class Animal implements animalInfo {
private age = 10;
setAge(age: number) {
this.age = age;
}
}
// D
class Animal implements animalInfo, animalBehavior {
private age = 10;
private food = 'bamboo';
setAge(age: number) {
this.age = age;
}
getFood() {
return this.food;
}
}
答案:
C D
解析:
类可以使用 implements
关键字实现接口,类需要实现接口中的所有方法。
- A - 类使用
implements
实现接口而不是extends
。故错误。 - B - 类
Animal
同时继承了接口animalInfo
和接口animalBehavior
,但是只实现了接口animalInfo
的setAge
方法,并未实现接口animalBehavior
的getFood
方法,故错误。 - C - 类
Animal
继承了接口animalInfo
,并实现了接口animalInfo
中的方法setAge
,用法正确。 - D - 类
Animal
同时继承了接口animalInfo
和接口animalBehavior
,并实现了接口中的所有方法,用法正确。
接口继承接口
在 TS 中接口这个概念非常灵活,除了类可以实现接口之外,一个接口也可以继承另一个接口,比如下面的例子:
interface Mouth {
sing(): void;
}
// 另一个接口继承前面的 Mouth
interface DragonMouth extends Mouth {
fire(): void;
}
class Dragon implements DragonMouth {
sing() {
console.log('龙在唱歌');
}
fire() {
console.log('龙在喷火');
}
}
习题:已知下面代码块,接口 animal
具体为?
interface animalInfo {
setAge(age: number): void;
}
interface animalBehavior {
getFood(): string;
}
interface animal extends animalInfo, animalBehavior {
setFeature(feature: string): void;
}
// A
interface animal {
setAge(age: number): void;
}
// B
interface animal {
setFeature(feature: string): void;
}
// C
interface animal {
setAge(age: number): void;
getFood(): string;
}
// D
interface animal {
setAge(age: number): void;
getFood(): string;
setFeature(feature: string): void;
}
答案:
D
解析:
接口可以使用 extends
关键字继承其他接口,一个接口可以继承多个接口。
题目中接口 animal
同时继承了接口 animalInfo
和接口 animalBehavior
,所以接口 animal
应该为
interface animal {
setAge(age: number): void;
getFood(): string;
setFeature(feature: string): void;
}
接口继承类
一个接口除了可以继承另一个接口之外,它可以继承一个类,比如下面的例子:
class Dragon {
fly() {
console.log('龙在飞');
}
}
interface FireDragon extends Dragon {
fire(): void;
}
let f: FireDragon = {
fire: function() {
console.log('龙在喷火');
},
fly() {
console.log('龙在飞');
}
}
习题:已知下面代码块,使用正确的是?
class AnimalInfo {
public age = 10;
public setAge(age: number) {
this.age = age;
}
}
interface animal extends AnimalInfo {
getFood(): string;
}
// A
const panda: animal = {
age: 20,
getFood: () => {
return 'bamboo';
},
setAge: (age: number) => {}
}
// B
const panda: animal = {
age: 20,
getFood: () => {
return 'bamboo';
}
}
// C
const panda: animal = {
getFood: () => {
return 'bamboo';
},
setAge: (age: number) => {}
}
// D
class panda: animal{
public age = 20;
public setAge(age: number) {
this.age = age;
};
public getFood() {
return 'bamboo';
}
}
答案:A
解析:
接口可以使用 extends
继承类。
题目中的接口 animal
继承自类 AnimalInfo
。我们可以理解为 animal
表示的接口为
interface animal {
age: number;
setAge: (age: number) => void;
getFood: () => void;
}
所以答案选 A
,D
是错误的语法哦!
资料:构造函数的类型
在 TypeScript 中,我们可以用 interface 来描述类,同时也可以用 interface 里特殊的 new() 来描述类的构造函数类型。使用方法如下:
// 用 INumber 来描述 NumberClass 的形状
interface INumber {
n: number;
double(n: number): number
}
// 构造一个 NumberClass 类
class NumberClass implements INumber {
constructor(public n: number) {
console.log('n is ' + n)
}
double(n: number) {
return 2 * n;
}
}
// 用 new() 来描述 NumberClass 的构造函数类型
interface INumberClassConstructable {
new(n: number): INumber
}
// 该方法接受一个类,并将之实例化,利用 INumberClassConstructable 就可以约束实例化时传递给 clazz 构造函数的参数是准确的
function buildNumber(clazz: INumberClassConstructable) {
return new clazz(1)
}