TypeScript 抽象类
抽象类(Abstract Class)是 TypeScript 面向对象编程中的重要概念。
它是一种不能被直接实例化的类,只能作为基类供其他类继承。
抽象类主要用于定义子类的公共属性和方法,为子类提供一个统一的结构和行为模板。
为什么需要抽象类
在面向对象编程中,我们经常需要定义一些基类。
这些基类描述了子类共同的属性和方法,但有些方法的实现细节需要由子类自己决定。
抽象类就是用来解决这个问题的:它允许我们定义方法的签名(声明),而把具体实现交给子类。
概念说明:抽象类是一种介于普通类和接口之间的类型。它既有接口的特征(定义方法签名),又有类的特征(可以有具体的方法实现和构造函数)。
抽象类基础
使用 abstract 关键字声明抽象类和抽象方法。
抽象类可以包含抽象方法和具象方法(已有实现的方法)。
实例
// 抽象类不能直接实例化,只能作为基类
abstract class Animal {
// 动物的名字属性
name: string;
// 构造函数
constructor(name: string) {
this.name = name;
}
// 抽象方法:使用 abstract 修饰,没有方法体
// 子类必须实现这个方法
abstract speak(): void;
// 具象方法:有具体实现的方法
// 子类可以直接继承使用,不需要重写
move(): void {
console.log(this.name + " 在移动");
}
}
// 尝试实例化抽象类会报错
// var animal = new Animal("动物"); // 错误:不能实例化抽象类
// 定义 Dog 类继承 Animal
class Dog extends Animal {
// 子类必须实现父类的抽象方法 speak()
speak(): void {
console.log(this.name + " 汪汪汪!");
}
}
// 创建 Dog 实例
var dog = new Dog("旺财");
dog.speak(); // 调用子类实现的方法
dog.move(); // 继承父类的具象方法
运行结果:
旺财 汪汪汪! 旺财 在移动
说明:抽象方法没有方法体(只有方法签名),子类必须实现该方法。具象方法有具体实现,子类可以直接继承使用。
抽象方法
抽象方法是抽象类中只声明但不实现的方法。
子类继承抽象类后,必须实现所有抽象方法,否则会报错。
实例
abstract class Shape {
// 抽象方法:只有声明,没有实现
// 子类必须实现这两个方法
abstract area(): number;
abstract perimeter(): number;
// 具象方法:可以使用抽象方法
// 这个方法调用了抽象方法,子类继承后可以正常使用
describe(): void {
console.log("面积: " + this.area().toFixed(2));
}
}
// 定义矩形类继承 Shape
class Rectangle extends Shape {
// 矩形的宽度
width: number;
// 矩形的高度
height: number;
// 构造函数
constructor(width: number, height: number) {
super(); // 调用父类构造函数
this.width = width;
this.height = height;
}
// 实现抽象方法:计算面积
area(): number {
return this.width * this.height;
}
// 实现抽象方法:计算周长
perimeter(): number {
return 2 * (this.width + this.height);
}
}
// 创建矩形实例
var rect = new Rectangle(4, 5);
// 调用继承的 describe 方法,内部会调用子类的 area 方法
rect.describe();
console.log("周长: " + rect.perimeter());
运行结果:
面积: 20.00 周长: 18
提示:具象方法可以调用抽象方法。这是因为当具象方法被调用时,子类已经实现了抽象方法,所以可以正常执行。
抽象类作为类型
抽象类可以作为参数类型使用。
这意味着函数可以接受任何抽象类的子类实例,实现了多态。
实例
abstract class Animal {
// 抽象方法:所有动物都会发出声音
abstract speak(): void;
}
// 定义 Cat 类继承 Animal
class Cat extends Animal {
// 实现抽象方法
speak(): void {
console.log("喵喵喵!");
}
}
// 定义 Dog 类继承 Animal
class Dog extends Animal {
// 实现抽象方法
speak(): void {
console.log("汪汪汪!");
}
}
// 定义函数参数类型为抽象类 Animal
// 这个函数可以接受任何 Animal 的子类实例
function makeSpeak(animal: Animal): void {
animal.speak();
}
// 传入不同的子类实例,实现不同的行为(多态)
makeSpeak(new Cat());
makeSpeak(new Dog());
// 抽象类类型数组:可以存储不同子类实例
var animals: Animal[] = [new Cat(), new Dog()];
运行结果:
喵喵喵! 汪汪汪!
多态:抽象类作为类型时,实际执行的是子类的方法实现。这就是面向对象的多态特性。
抽象类与接口的区别
抽象类和接口都用于定义类型规范,但有一些关键区别。
理解它们的差异有助于在实际开发中做出正确的选择。
| 特性 | 抽象类 | 接口 |
|---|---|---|
| 实例化 | 不能直接实例化 | 不能直接实例化 |
| 方法实现 | 可以有具体实现 | 只能有声明(TypeScript 3.6+ 可有默认实现) |
| 成员修饰符 | 可以添加 public、protected、private | 只能有 readonly |
| 继承 | 单继承(只能 extends 一个类) | 多实现(可以 implements 多个接口) |
| 构造函数 | 可以有构造函数 | 不能有构造函数 |
选择建议:需要共享代码使用(用抽象类);需要定义规范/契约(用接口);需要多继承(用接口)。
完整示例:支付系统
下面是一个使用抽象类实现的支付系统示例。
这个示例展示了抽象类在实际项目中的应用。
实例
abstract class Payment {
// 抽象方法:处理支付,子类必须实现
abstract process(amount: number): boolean;
// 具象方法:验证支付信息
// 所有子类都可以使用这个方法
validate(): void {
console.log("验证支付信息");
}
}
// 信用卡支付类
class CreditCardPayment extends Payment {
// 信用卡号码
cardNumber: string;
// 构造函数
constructor(cardNumber: string) {
super(); // 调用父类构造函数
this.cardNumber = cardNumber;
}
// 实现抽象方法:处理信用卡支付
process(amount: number): boolean {
console.log("处理信用卡支付: " + amount);
return true;
}
}
// PayPal 支付类
class PayPalPayment extends Payment {
// PayPal 邮箱
email: string;
// 构造函数
constructor(email: string) {
super();
this.email = email;
}
// 实现抽象方法:处理 PayPal 支付
process(amount: number): boolean {
console.log("处理 PayPal 支付: " + amount);
return true;
}
}
// 使用多态处理不同的支付方式
var payments: Payment[] = [
new CreditCardPayment("1234"),
new PayPalPayment("test@example.com")
];
// 遍历处理每种支付方式
for (var _i = 0, payments_1 = payments; _i < payments_1.length; _i++) {
var payment = payments_1[_i];
// 调用继承的验证方法
payment.validate();
// 调用子类的处理方法
payment.process(100);
}
运行结果:
验证支付信息 处理信用卡支付: 100 验证支付信息 处理 PayPal 支付: 100
应用场景:抽象类常用于框架设计和业务逻辑分层。例如支付处理、用户认证、数据持久化等场景。
注意事项
- 不能实例化:抽象类不能直接使用 new 创建实例,必须通过子类继承
- 必须实现抽象方法:子类如果不实现所有抽象方法,会报错
- 可以有构造函数:抽象类可以定义构造函数,子类需要调用 super()
- 单继承:一个类只能继承一个抽象类(单继承)
- 访问修饰符:抽象方法可以使用 public、protected 修饰符
最佳实践:当多个类需要共享代码和逻辑时,使用抽象类。当只需要定义规范和契约时,使用接口。
总结
抽象类是 TypeScript 面向对象编程的重要组成部分。
- abstract 关键字:用于声明抽象类和方法
- 不能实例化:只能通过子类继承使用
- 抽象方法:子类必须实现的方法
- 具象方法:子类可以直接继承使用的有实现的方法
- 模板方法模式:抽象类定义骨架,子类提供具体实现
建议:在设计类层次结构时,优先考虑使用抽象类来共享代码和定义规范。
