类的继承和多态
欢迎继续本专栏的第十四篇文章。在前几期中,我们已逐步建立了 TypeScript 类的基础知识,包括类的定义、构造函数、属性和方法,以及访问修饰符的运用。这些内容为我们理解面向对象编程的核心原则奠定了基础。今天,我们将深入探讨类的继承和多态这两个关键概念,它们是实现代码复用和灵活设计的强大机制。我们将解释 extends 关键字的使用、方法重写的技巧,以及 super 调用的作用,并通过这些元素展示如何实现代码复用和多态行为。通过由浅入深的讲解、丰富示例和实际场景分析,我们旨在帮助您从基本继承语法逐步掌握高级应用,并在项目中运用这些原则来构建更高效和可扩展的代码结构。内容将从继承的基础概念展开到多态的实践,确保您能获得全面而深刻的洞见。
理解继承和多态在 TypeScript 中的定位
继承(inheritance)是面向对象编程的支柱之一,它允许一个类(子类)从另一个类(父类)获取属性和方法,从而实现代码复用。多态(polymorphism)则基于继承,让不同类的对象以统一方式响应相同消息,实现行为多样性。在 JavaScript 中,继承基于原型链,而 TypeScript 通过类语法和类型系统增强了这一机制,使继承更安全和直观。
继承的定位在于促进"is-a"关系:例如,Dog is-a Animal,这让子类继承父类的通用行为,同时添加特定功能。这减少了重复代码,提高了维护性。多态则允许代码处理父类引用,但执行子类行为,实现"一个接口,多种实现"。在 TypeScript 中,这些概念通过静态类型检查得到强化:编译器确保继承兼容性和方法签名一致,避免运行时意外。
为什么继承和多态重要?在大型项目中,如游戏引擎或企业应用,继承组织层次结构,多态处理变体。根据 TypeScript 的设计,继承与接口结合:类通过 extends 继承,implements 实现接口。这平衡了动态 JS 与静态安全的需要。我们将从 extends 的基本使用开始,逐步引入方法重写、super 调用,并探讨复用和多态的应用,确保您能理解如何避免继承滥用,同时发挥其优势。
继承和多态在 TypeScript 中的历史与 ES6 类同步引入,并在后续版本优化了类型推断。这让它们成为连接过程式和 OOP 的工具,在现代开发中帮助管理复杂状态和行为。
继承的基本语法:extends 关键字的使用
继承使用 extends 关键字,让子类从父类获取成员。子类可访问父类的 public 和 protected 成员,但不包括 private。
extends 的基本定义与简单示例
基础继承:
typescript
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
move(distance: number): void {
console.log(`${this.name} moved ${distance} meters.`);
}
}
class Dog extends Animal {
bark(): void {
console.log("Woof!");
}
}
这里,Dog 继承 Animal 的 name、constructor 和 move。创建实例:
typescript
const myDog = new Dog("Buddy");
myDog.move(10); // "Buddy moved 10 meters."
myDog.bark(); // "Woof!"
new Dog 调用 Animal 的构造函数(隐式 super),然后添加 bark。
无构造函数子类:
子类默认调用父类 constructor。
typescript
class Cat extends Animal {} // 继承所有
const myCat = new Cat("Whiskers");
myCat.move(5); // 有效
基本语法让继承直观,重用父类代码。
extends 的深入机制
多级继承:
typescript
class Mammal extends Animal {
hasFur: boolean = true;
}
class Rabbit extends Mammal {
jump(): void {
console.log("Hop!");
}
}
Rabbit 有 Animal 的 move、Mammal 的 hasFur 和自己的 jump。
静态成员继承:子类继承父类静态。
typescript
class Base {
static count: number = 0;
}
class Derived extends Base {}
console.log(Derived.count); // 0
深入:extends 要求子类兼容父类类型(协变/逆变规则)。参数类型可更宽,返回更窄。
extends 机制确保类型安全继承,适合建模层次,如 UI 组件基类。
方法重写:自定义子类行为
方法重写(override)允许子类重新定义父类方法,实现特定行为。
方法重写的的基本用法
typescript
class Bird extends Animal {
move(distance: number): void {
console.log(`${this.name} flew ${distance} meters.`);
}
}
Bird 重写 move:
typescript
const sparrow = new Bird("Tweetie");
sparrow.move(20); // "Tweetie flew 20 meters."
重写必须签名兼容:参数和返回相同。
方法重写的深入应用
重写 protected 方法:
父类 protected 可在子类重写。
typescript
class Vehicle {
protected startEngine(): void {
console.log("Engine started.");
}
drive(): void {
this.startEngine();
console.log("Driving.");
}
}
class ElectricVehicle extends Vehicle {
protected startEngine(): void {
console.log("Battery activated.");
}
}
ElectricVehicle 重写 startEngine,drive 调用新版。
重写 getter/setter:
typescript
class BaseShape {
get area(): number {
return 0;
}
}
class Square extends BaseShape {
side: number;
constructor(side: number) {
super();
this.side = side;
}
get area(): number {
return this.side ** 2;
}
}
深入重写让子类定制行为,支持多态(后述)。
风险:重写改变语义导致 bug。实践:文档重写意图。
super 调用:访问父类成员
super 关键字调用父类成员,用于构造函数或方法。
super 在构造函数中的基本用法
子类构造函数必须调用 super() 访问父类初始化。
typescript
class Employee extends Person {
position: string;
constructor(name: string, age: number, position: string) {
super(name, age); // 调用父类 constructor
this.position = position;
}
}
无 super 报错。
super 在方法中的深入应用
super 调用父类方法:
typescript
class Snake extends Animal {
move(distance: number): void {
console.log("Slithering...");
super.move(distance); // 调用父类 move
}
}
Snake.move 先自定义,然后父类行为。
super 在静态:
typescript
class Child extends Base {
static getCount(): number {
return super.count; // 访问父静态
}
}
深入 super 确保继承链完整,结合重写实现增量行为。
应用:日志类,重写 log 调用 super 添加时间戳。
实现代码复用:继承的核心益处
继承通过共享成员实现复用,减少重复。
代码复用的基本示例
基类共享逻辑:
typescript
class Logger {
log(message: string): void {
console.log(`[INFO] ${message}`);
}
}
class ErrorLogger extends Logger {
logError(message: string): void {
this.log(`Error: ${message}`);
}
}
ErrorLogger 复用 log 添加错误处理。
代码复用的深入策略
模板方法模式:父类定义框架,子类填充细节。
typescript
abstract class Beverage {
prepare(): void {
this.boilWater();
this.brew();
this.pourInCup();
this.addCondiments();
}
boilWater(): void { console.log("Boiling water"); }
abstract brew(): void;
pourInCup(): void { console.log("Pouring into cup"); }
abstract addCondiments(): void;
}
class Tea extends Beverage {
brew(): void { console.log("Steeping tea"); }
addCondiments(): void { console.log("Adding lemon"); }
}
Tea 复用 prepare 框架,定义具体步骤。
复用属性:共享状态管理。
深入复用让代码 DRY,在框架如 Angular 服务继承中常见。
多态行为:统一接口,多样实现
多态让父类引用执行子类行为。
多态的基本示例
typescript
function makeAnimalMove(animal: Animal): void {
animal.move(10);
}
makeAnimalMove(new Dog("Buddy")); // Dog 的 move
makeAnimalMove(new Bird("Tweetie")); // Bird 的重写 move
函数用 Animal 类型,但调用子类实现。
多态的深入应用
数组多态:
typescript
const animals: Animal[] = [new Dog("Buddy"), new Bird("Tweetie")];
animals.forEach(a => a.move(5)); // 各执行自己 move
策略模式:多态替换 if-else。
typescript
interface PaymentStrategy {
pay(amount: number): void;
}
class CreditCard implements PaymentStrategy {
pay(amount: number): void { console.log(`Paid ${amount} via credit card`); }
}
class PayPal implements PaymentStrategy {
pay(amount: number): void { console.log(`Paid ${amount} via PayPal`); }
}
class Checkout {
constructor(private strategy: PaymentStrategy) {}
complete(amount: number): void {
this.strategy.pay(amount);
}
}
多态让策略可换。
深入多态提升灵活,在插件系统或 UI 渲染中关键。
实际应用:继承与多态在项目中的实践
继承在模型层次:
基类 Entity,子类 User、Product 继承 id、timestamps。
多态在事件处理:基类 EventHandler,子类处理不同事件。
案例:电商系统,Product 基类,Book、Electronics 子类,多态计算价格。
OOP 实践组织代码,易扩展。
高级主题:抽象类与接口结合
抽象类用 abstract,提供部分实现。
如 Beverage 示例。
接口多实现:
类可 implements 多接口,extends 单类。
typescript
interface Flyable {
fly(): void;
}
class FlyingCar extends Car implements Flyable {
fly(): void { /* ... */ }
}
高级增强继承。
风险与最佳实践
风险:
- 继承滥用导致紧耦合。
- 重写破坏 Liskov 替换。
- super 遗漏初始化错误。
实践:
- 组合优先继承。
- 浅继承链。
- 测试多态。
- 文档重写。
确保可靠。
案例研究:真实项目
在 React,组件继承共享状态。
在 Node,服务类继承基控制器。
减少冗余 25%。
结语:继承与多态,OOP 的动态力量
通过本篇文章的详尽探讨,您已掌握类的继承和多态,从 extends 到 super。这些原则将助您实现复用和灵活。实践:构建继承 hierarchy。下一期抽象类与接口实现,敬请期待。若疑问,欢迎交流。我们继续。