Skip to content

抽象类

概述

抽象类(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)
  • 抽象属性也必须在子类中实现

重要

  • 抽象类主要用于定义契约和共享实现,不能直接使用
  • 抽象类与接口的区别:抽象类可以包含实现,接口只能定义契约
  • 抽象类支持单继承,接口支持多实现
  • 合理使用抽象类可以提高代码的复用性和可维护性
  • 抽象类适合用于需要共享实现代码的场景

信息

抽象类的应用场景

  1. 模板方法模式:定义算法骨架,子类实现具体步骤
  2. 代码复用:多个类有共同实现,但某些方法需要子类定制
  3. 接口契约:定义子类必须实现的方法
  4. 框架设计:为框架使用者提供基础类和扩展点

抽象类 vs 接口

  • 使用抽象类:需要共享实现代码、需要构造函数、需要访问修饰符
  • 使用接口:只需要定义契约、需要多继承、更轻量级

设计原则

  • 优先使用接口定义契约
  • 需要共享实现时使用抽象类
  • 可以同时使用抽象类和接口

相关链接

基于 VitePress 构建