抽象类
概述
抽象类(Abstract Class)是 TypeScript 中一种特殊的类,它不能被直接实例化,只能作为其他类的基类使用。抽象类使用 abstract 关键字定义,可以包含抽象方法(没有实现的方法)和具体实现的方法。抽象类的主要目的是为子类提供一个通用的模板,定义子类必须实现的方法,同时可以包含一些通用的实现。抽象类在面向对象编程中用于实现模板方法模式、定义接口契约等场景,是代码复用和设计模式的重要工具。
基本语法
抽象类定义
使用 abstract 关键字定义抽象类:
typescript
// 抽象类定义
abstract class Animal {
protected name: string;
constructor(name: string) {
this.name = name;
}
// 抽象方法:子类必须实现
abstract makeSound(): void;
// 具体方法:可以有实现
move(): void {
console.log(`${this.name} is moving`);
}
// 具体方法:可以有实现
getName(): string {
return this.name;
}
}
// ❌ 错误:不能直接实例化抽象类
// const animal = new Animal("Generic");
// Error: Cannot create an instance of an abstract class
// ✅ 正确:创建子类并实现抽象方法
class Dog extends Animal {
constructor(name: string) {
super(name);
}
// 必须实现抽象方法
makeSound(): void {
console.log(`${this.name} barks: Woof!`);
}
}
const dog = new Dog("Buddy");
dog.makeSound(); // "Buddy barks: Woof!"
dog.move(); // "Buddy is moving"(继承自父类)抽象方法
抽象方法使用 abstract 关键字声明,没有方法体,必须在子类中实现:
typescript
abstract class Shape {
protected color: string;
constructor(color: string) {
this.color = color;
}
// 抽象方法:计算面积(子类必须实现)
abstract getArea(): number;
// 抽象方法:计算周长(子类必须实现)
abstract getPerimeter(): number;
// 具体方法:可以有实现
getColor(): string {
return this.color;
}
// 具体方法:可以有实现
describe(): string {
return `A ${this.color} shape with area ${this.getArea()} and perimeter ${this.getPerimeter()}`;
}
}
// 实现抽象类
class Circle extends Shape {
private radius: number;
constructor(color: string, radius: number) {
super(color);
this.radius = radius;
}
// 实现抽象方法
getArea(): number {
return Math.PI * this.radius * this.radius;
}
// 实现抽象方法
getPerimeter(): number {
return 2 * Math.PI * this.radius;
}
}
// 实现抽象类
class Rectangle extends Shape {
private width: number;
private height: number;
constructor(color: string, width: number, height: number) {
super(color);
this.width = width;
this.height = height;
}
// 实现抽象方法
getArea(): number {
return this.width * this.height;
}
// 实现抽象方法
getPerimeter(): number {
return 2 * (this.width + this.height);
}
}
// 使用示例
const circle = new Circle("red", 5);
const rectangle = new Rectangle("blue", 4, 6);
console.log(circle.describe()); // "A red shape with area 78.54 and perimeter 31.42"
console.log(rectangle.describe()); // "A blue shape with area 24 and perimeter 20"注意
抽象方法只能在抽象类中声明。如果类中有抽象方法,该类必须是抽象类。
抽象类与具体实现
抽象类可以同时包含抽象方法和具体实现的方法:
typescript
abstract class Database {
protected connectionString: string;
constructor(connectionString: string) {
this.connectionString = connectionString;
}
// 抽象方法:连接数据库(子类必须实现)
abstract connect(): Promise<void>;
// 抽象方法:断开连接(子类必须实现)
abstract disconnect(): Promise<void>;
// 抽象方法:执行查询(子类必须实现)
abstract query(sql: string): Promise<any[]>;
// 具体方法:通用实现
protected log(message: string): void {
console.log(`[Database] ${new Date().toISOString()}: ${message}`);
}
// 具体方法:通用实现
public async executeTransaction(queries: string[]): Promise<void> {
this.log("Starting transaction");
try {
await this.connect();
for (const sql of queries) {
await this.query(sql);
}
this.log("Transaction completed");
} catch (error) {
this.log(`Transaction failed: ${error}`);
throw error;
} finally {
await this.disconnect();
}
}
}
// 实现 MySQL 数据库
class MySQLDatabase extends Database {
async connect(): Promise<void> {
this.log(`Connecting to MySQL: ${this.connectionString}`);
// 实际的 MySQL 连接逻辑
}
async disconnect(): Promise<void> {
this.log("Disconnecting from MySQL");
// 实际的 MySQL 断开逻辑
}
async query(sql: string): Promise<any[]> {
this.log(`Executing MySQL query: ${sql}`);
// 实际的 MySQL 查询逻辑
return [];
}
}
// 实现 PostgreSQL 数据库
class PostgreSQLDatabase extends Database {
async connect(): Promise<void> {
this.log(`Connecting to PostgreSQL: ${this.connectionString}`);
// 实际的 PostgreSQL 连接逻辑
}
async disconnect(): Promise<void> {
this.log("Disconnecting from PostgreSQL");
// 实际的 PostgreSQL 断开逻辑
}
async query(sql: string): Promise<any[]> {
this.log(`Executing PostgreSQL query: ${sql}`);
// 实际的 PostgreSQL 查询逻辑
return [];
}
}
// 使用示例
const mysql = new MySQLDatabase("mysql://localhost:3306/mydb");
const postgres = new PostgreSQLDatabase("postgresql://localhost:5432/mydb");
// 两种数据库都可以使用通用的事务方法
await mysql.executeTransaction(["SELECT * FROM users", "UPDATE users SET status = 'active'"]);
await postgres.executeTransaction(["SELECT * FROM products", "UPDATE products SET price = 100"]);抽象属性
TypeScript 也支持抽象属性,子类必须实现这些属性:
typescript
abstract class Vehicle {
// 抽象属性:子类必须实现
abstract brand: string;
abstract model: string;
// 具体属性
protected speed: number = 0;
// 抽象方法
abstract start(): void;
abstract stop(): void;
// 具体方法
getSpeed(): number {
return this.speed;
}
getInfo(): string {
return `${this.brand} ${this.model}`;
}
}
class Car extends Vehicle {
// 实现抽象属性
brand: string;
model: string;
constructor(brand: string, model: string) {
super();
this.brand = brand;
this.model = model;
}
// 实现抽象方法
start(): void {
console.log(`Starting ${this.getInfo()}...`);
this.speed = 10;
}
// 实现抽象方法
stop(): void {
console.log(`Stopping ${this.getInfo()}...`);
this.speed = 0;
}
}
const car = new Car("Toyota", "Camry");
car.start(); // "Starting Toyota Camry..."
console.log(car.getSpeed()); // 10
console.log(car.getInfo()); // "Toyota Camry"抽象类与接口的区别
抽象类和接口都可以定义契约,但它们有重要区别:
接口
typescript
// 接口:只能定义契约,不能有实现
interface Flyable {
fly(): void;
}
interface Swimmable {
swim(): void;
}
// 类可以实现多个接口
class Duck implements Flyable, Swimmable {
fly(): void {
console.log("Duck is flying");
}
swim(): void {
console.log("Duck is swimming");
}
}抽象类
typescript
// 抽象类:可以包含实现,但只能单继承
abstract class Animal {
protected name: string;
constructor(name: string) {
this.name = name;
}
// 可以有具体实现
eat(): void {
console.log(`${this.name} is eating`);
}
// 抽象方法
abstract makeSound(): void;
}
class Dog extends Animal {
makeSound(): void {
console.log(`${this.name} barks`);
}
}对比总结
| 特性 | 抽象类 | 接口 |
|---|---|---|
| 实例化 | ❌ 不能实例化 | ❌ 不能实例化 |
| 实现 | ✅ 可以包含具体实现 | ❌ 只能定义契约 |
| 继承 | ✅ 单继承 | ✅ 可以实现多个接口 |
| 构造函数 | ✅ 可以有构造函数 | ❌ 不能有构造函数 |
| 访问修饰符 | ✅ 支持 public、protected、private | ✅ 默认 public |
| 属性初始化 | ✅ 可以初始化属性 | ❌ 不能初始化属性 |
提示
- 如果需要共享实现代码,使用抽象类
- 如果需要定义契约且可以多继承,使用接口
- 可以同时使用抽象类和接口
使用示例
示例 1:支付系统
typescript
// 抽象支付类
abstract class PaymentMethod {
protected amount: number;
protected currency: string;
protected transactionId: string;
constructor(amount: number, currency: string = "USD") {
this.amount = amount;
this.currency = currency;
this.transactionId = this.generateTransactionId();
}
// 抽象方法:处理支付(子类必须实现)
abstract process(): Promise<boolean>;
// 抽象方法:验证支付信息(子类必须实现)
abstract validate(): boolean;
// 抽象方法:获取支付信息(子类必须实现)
abstract getPaymentInfo(): string;
// 具体方法:生成交易 ID
protected generateTransactionId(): string {
return `TXN-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
}
// 具体方法:记录交易
protected logTransaction(status: string): void {
console.log(`[${new Date().toISOString()}] Transaction ${this.transactionId}: ${status}`);
}
// 具体方法:执行支付流程
public async execute(): Promise<boolean> {
this.logTransaction("Started");
if (!this.validate()) {
this.logTransaction("Validation failed");
return false;
}
const result = await this.process();
if (result) {
this.logTransaction("Completed successfully");
} else {
this.logTransaction("Failed");
}
return result;
}
// 具体方法:获取交易信息
public getTransactionInfo(): string {
return `Transaction ID: ${this.transactionId}, Amount: ${this.currency} ${this.amount}`;
}
}
// 信用卡支付实现
class CreditCardPayment extends PaymentMethod {
private cardNumber: string;
private cardHolder: string;
private expiryDate: string;
private cvv: string;
constructor(
amount: number,
cardNumber: string,
cardHolder: string,
expiryDate: string,
cvv: string,
currency: string = "USD"
) {
super(amount, currency);
this.cardNumber = cardNumber;
this.cardHolder = cardHolder;
this.expiryDate = expiryDate;
this.cvv = cvv;
}
validate(): boolean {
// 验证卡号格式
if (this.cardNumber.length !== 16 || !/^\d+$/.test(this.cardNumber)) {
return false;
}
// 验证 CVV
if (this.cvv.length !== 3 || !/^\d+$/.test(this.cvv)) {
return false;
}
// 验证过期日期
const [month, year] = this.expiryDate.split('/');
const expiry = new Date(2000 + parseInt(year), parseInt(month) - 1);
if (expiry < new Date()) {
return false;
}
return true;
}
async process(): Promise<boolean> {
// 模拟支付处理
console.log(`Processing credit card payment for ${this.cardHolder}`);
console.log(`Card: ****${this.cardNumber.slice(-4)}`);
// 实际的支付处理逻辑
return true;
}
getPaymentInfo(): string {
return `Credit Card: ****${this.cardNumber.slice(-4)} - ${this.cardHolder}`;
}
}
// 支付宝支付实现
class AlipayPayment extends PaymentMethod {
private accountId: string;
constructor(amount: number, accountId: string, currency: string = "CNY") {
super(amount, currency);
this.accountId = accountId;
}
validate(): boolean {
// 验证支付宝账号格式
return /^[\w\.-]+@[\w\.-]+\.\w+$/.test(this.accountId) ||
/^1[3-9]\d{9}$/.test(this.accountId);
}
async process(): Promise<boolean> {
// 模拟支付处理
console.log(`Processing Alipay payment for account ${this.accountId}`);
// 实际的支付处理逻辑
return true;
}
getPaymentInfo(): string {
return `Alipay: ${this.accountId}`;
}
}
// 使用示例
const creditPayment = new CreditCardPayment(
100,
"1234567890123456",
"John Doe",
"12/25",
"123"
);
const alipayPayment = new AlipayPayment(500, "alice@example.com");
// 两种支付方式都使用相同的执行流程
await creditPayment.execute();
console.log(creditPayment.getTransactionInfo());
console.log(creditPayment.getPaymentInfo());
await alipayPayment.execute();
console.log(alipayPayment.getTransactionInfo());
console.log(alipayPayment.getPaymentInfo());示例 2:文件处理系统
typescript
// 抽象文件处理器
abstract class FileProcessor {
protected filePath: string;
protected fileSize: number = 0;
constructor(filePath: string) {
this.filePath = filePath;
}
// 抽象方法:读取文件(子类必须实现)
abstract read(): Promise<string>;
// 抽象方法:写入文件(子类必须实现)
abstract write(content: string): Promise<void>;
// 抽象方法:验证文件格式(子类必须实现)
abstract validateFormat(): boolean;
// 具体方法:获取文件信息
public getFileInfo(): string {
return `File: ${this.filePath}, Size: ${this.fileSize} bytes`;
}
// 具体方法:处理文件(模板方法模式)
public async process(): Promise<void> {
console.log(`Processing file: ${this.filePath}`);
if (!this.validateFormat()) {
throw new Error("Invalid file format");
}
const content = await this.read();
const processedContent = this.transform(content);
await this.write(processedContent);
console.log("File processed successfully");
}
// 具体方法:转换内容(可以被子类重写)
protected transform(content: string): string {
return content; // 默认不转换
}
}
// JSON 文件处理器
class JSONFileProcessor extends FileProcessor {
validateFormat(): boolean {
return this.filePath.endsWith('.json');
}
async read(): Promise<string> {
// 模拟读取 JSON 文件
console.log(`Reading JSON file: ${this.filePath}`);
this.fileSize = 1024;
return '{"name": "John", "age": 30}';
}
async write(content: string): Promise<void> {
// 模拟写入 JSON 文件
console.log(`Writing JSON file: ${this.filePath}`);
console.log(`Content: ${content}`);
}
// 重写转换方法:美化 JSON
protected transform(content: string): string {
try {
const obj = JSON.parse(content);
return JSON.stringify(obj, null, 2);
} catch {
return content;
}
}
}
// CSV 文件处理器
class CSVFileProcessor extends FileProcessor {
validateFormat(): boolean {
return this.filePath.endsWith('.csv');
}
async read(): Promise<string> {
// 模拟读取 CSV 文件
console.log(`Reading CSV file: ${this.filePath}`);
this.fileSize = 2048;
return 'name,age,city\nJohn,30,New York\nAlice,25,London';
}
async write(content: string): Promise<void> {
// 模拟写入 CSV 文件
console.log(`Writing CSV file: ${this.filePath}`);
console.log(`Content:\n${content}`);
}
// 重写转换方法:转换为大写
protected transform(content: string): string {
return content.toUpperCase();
}
}
// 使用示例
const jsonProcessor = new JSONFileProcessor("data.json");
const csvProcessor = new CSVFileProcessor("data.csv");
await jsonProcessor.process();
console.log(jsonProcessor.getFileInfo());
await csvProcessor.process();
console.log(csvProcessor.getFileInfo());示例 3:游戏角色系统
typescript
// 抽象角色类
abstract class Character {
protected name: string;
protected health: number;
protected maxHealth: number;
protected level: number = 1;
constructor(name: string, maxHealth: number) {
this.name = name;
this.maxHealth = maxHealth;
this.health = maxHealth;
}
// 抽象方法:攻击(子类必须实现)
abstract attack(target: Character): number;
// 抽象方法:防御(子类必须实现)
abstract defend(damage: number): number;
// 抽象方法:使用技能(子类必须实现)
abstract useSkill(): void;
// 具体方法:受到伤害
public takeDamage(damage: number): void {
const actualDamage = this.defend(damage);
this.health = Math.max(0, this.health - actualDamage);
console.log(`${this.name} took ${actualDamage} damage. Health: ${this.health}/${this.maxHealth}`);
if (this.health === 0) {
console.log(`${this.name} has been defeated!`);
}
}
// 具体方法:治疗
public heal(amount: number): void {
this.health = Math.min(this.maxHealth, this.health + amount);
console.log(`${this.name} healed ${amount} HP. Health: ${this.health}/${this.maxHealth}`);
}
// 具体方法:升级
public levelUp(): void {
this.level++;
this.maxHealth += 10;
this.health = this.maxHealth;
console.log(`${this.name} leveled up to level ${this.level}!`);
}
// 具体方法:获取状态
public getStatus(): string {
return `${this.name} (Lv.${this.level}) - HP: ${this.health}/${this.maxHealth}`;
}
// 具体方法:是否存活
public isAlive(): boolean {
return this.health > 0;
}
}
// 战士类
class Warrior extends Character {
private strength: number;
private armor: number;
constructor(name: string, strength: number = 10, armor: number = 5) {
super(name, 100);
this.strength = strength;
this.armor = armor;
}
attack(target: Character): number {
const damage = this.strength + Math.floor(Math.random() * 5);
console.log(`${this.name} attacks with ${damage} damage!`);
target.takeDamage(damage);
return damage;
}
defend(damage: number): number {
const reducedDamage = Math.max(1, damage - this.armor);
return reducedDamage;
}
useSkill(): void {
console.log(`${this.name} uses "Power Strike"!`);
this.strength += 5;
console.log(`Strength increased to ${this.strength}`);
}
}
// 法师类
class Mage extends Character {
private magicPower: number;
private mana: number;
constructor(name: string, magicPower: number = 15, mana: number = 50) {
super(name, 60);
this.magicPower = magicPower;
this.mana = mana;
}
attack(target: Character): number {
if (this.mana < 10) {
console.log(`${this.name} doesn't have enough mana!`);
return 0;
}
const damage = this.magicPower + Math.floor(Math.random() * 10);
this.mana -= 10;
console.log(`${this.name} casts a spell dealing ${damage} damage! (Mana: ${this.mana})`);
target.takeDamage(damage);
return damage;
}
defend(damage: number): number {
// 法师防御力较低
return damage;
}
useSkill(): void {
if (this.mana < 20) {
console.log(`${this.name} doesn't have enough mana for skill!`);
return;
}
this.mana -= 20;
console.log(`${this.name} uses "Fireball"!`);
this.magicPower += 3;
console.log(`Magic power increased to ${this.magicPower}`);
}
}
// 使用示例
const warrior = new Warrior("Conan", 12, 6);
const mage = new Mage("Gandalf", 18, 60);
console.log(warrior.getStatus()); // "Conan (Lv.1) - HP: 100/100"
console.log(mage.getStatus()); // "Gandalf (Lv.1) - HP: 60/60"
// 战斗
warrior.attack(mage); // 战士攻击法师
mage.attack(warrior); // 法师攻击战士
warrior.useSkill(); // 战士使用技能
mage.useSkill(); // 法师使用技能
mage.heal(20); // 法师治疗
console.log(warrior.getStatus());
console.log(mage.getStatus());类型检查示例
常见错误
typescript
// ❌ 错误:尝试实例化抽象类
abstract class Animal {
abstract makeSound(): void;
}
const animal = new Animal();
// Error: Cannot create an instance of an abstract class
// ❌ 错误:子类没有实现所有抽象方法
abstract class Shape {
abstract getArea(): number;
abstract getPerimeter(): number;
}
class Circle extends Shape {
private radius: number;
constructor(radius: number) {
super();
this.radius = radius;
}
getArea(): number {
return Math.PI * this.radius * this.radius;
}
// 错误:缺少 getPerimeter 方法的实现
// Error: Non-abstract class 'Circle' does not implement inherited abstract member 'getPerimeter' from class 'Shape'
}
// ❌ 错误:在非抽象类中声明抽象方法
class Example {
abstract method(): void;
// Error: Abstract methods can only appear within an abstract class
}
// ❌ 错误:抽象方法不能有实现
abstract class Base {
abstract method(): void {
console.log("Implementation");
// Error: Method 'method' cannot have an implementation because it is marked abstract
}
}正确写法
typescript
// ✅ 正确:创建子类并实现所有抽象方法
abstract class Animal {
protected name: string;
constructor(name: string) {
this.name = name;
}
abstract makeSound(): void;
move(): void {
console.log(`${this.name} is moving`);
}
}
class Dog extends Animal {
constructor(name: string) {
super(name);
}
makeSound(): void {
console.log(`${this.name} barks`);
}
}
const dog = new Dog("Buddy");
dog.makeSound(); // ✅ "Buddy barks"
dog.move(); // ✅ "Buddy is moving"
// ✅ 正确:实现所有抽象方法和属性
abstract class Shape {
abstract color: string;
abstract getArea(): number;
abstract getPerimeter(): number;
}
class Circle extends Shape {
color: string;
private radius: number;
constructor(color: string, radius: number) {
super();
this.color = color;
this.radius = radius;
}
getArea(): number {
return Math.PI * this.radius * this.radius;
}
getPerimeter(): number {
return 2 * Math.PI * this.radius;
}
}
const circle = new Circle("red", 5);
console.log(circle.getArea()); // ✅ 78.54
// ✅ 正确:抽象方法可以有访问修饰符
abstract class Base {
public abstract publicMethod(): void;
protected abstract protectedMethod(): void;
}
class Derived extends Base {
public publicMethod(): void {
console.log("Public method");
}
protected protectedMethod(): void {
console.log("Protected method");
}
}注意事项
提示
- 抽象类使用
abstract关键字定义 - 抽象类不能被直接实例化,只能作为基类
- 抽象方法必须在子类中实现
- 抽象类可以同时包含抽象方法和具体实现的方法
- 抽象类适合用于定义模板和共享实现代码
- 使用抽象类可以实现模板方法模式
注意
- 如果类中有抽象方法,该类必须是抽象类
- 子类必须实现父类的所有抽象方法,否则子类也必须是抽象类
- 抽象方法不能有方法体(实现)
- 抽象类可以有构造函数,但只能通过子类的
super()调用 - 抽象类支持访问修饰符(public、protected、private)
- 抽象属性也必须在子类中实现
重要
- 抽象类主要用于定义契约和共享实现,不能直接使用
- 抽象类与接口的区别:抽象类可以包含实现,接口只能定义契约
- 抽象类支持单继承,接口支持多实现
- 合理使用抽象类可以提高代码的复用性和可维护性
- 抽象类适合用于需要共享实现代码的场景
信息
抽象类的应用场景:
- 模板方法模式:定义算法骨架,子类实现具体步骤
- 代码复用:多个类有共同实现,但某些方法需要子类定制
- 接口契约:定义子类必须实现的方法
- 框架设计:为框架使用者提供基础类和扩展点
抽象类 vs 接口:
- 使用抽象类:需要共享实现代码、需要构造函数、需要访问修饰符
- 使用接口:只需要定义契约、需要多继承、更轻量级
设计原则:
- 优先使用接口定义契约
- 需要共享实现时使用抽象类
- 可以同时使用抽象类和接口
相关链接
- 类基础 - 了解类的基础概念
- 类继承 - 学习类的继承机制
- 访问修饰符 - 详细了解访问修饰符的使用
- 接口 - 了解接口的定义和使用,对比抽象类与接口的区别
- TypeScript 官方文档 - 抽象类