ZetCode

Dart 中的抽象类

最后修改于 2025 年 5 月 25 日

本教程探讨 Dart 中的抽象类,这是创建类层次结构和定义接口的关键功能。抽象类能够实现多态和代码重用,同时强制执行实现契约。

抽象类概述

Dart 中的抽象类是无法直接实例化的不完整蓝图。它们定义了一个具体子类必须实现的契约。抽象类可以包含已实现的方法和抽象方法的声明。

与普通类不同,抽象类可以包含未实现的方法(抽象方法)。任何具体的(非抽象)子类都必须实现这些方法。抽象类使用 abstract 修饰符声明。

功能 抽象类 普通类
实例化 不能实例化 可以实例化
方法 可以包含抽象方法 所有方法都已实现
目的 定义接口/行为 完整实现
继承 必须继承 可直接使用

当您想在多个相关类之间共享代码,同时强制执行某些方法的实现时,抽象类特别有用。它们介于接口(纯契约)和具体类(完整实现)之间。

基本抽象类

本示例演示了一个具有具体方法和抽象方法的简单抽象类。我们将创建一个抽象的 Shape 类,它为所有形状定义了通用行为。

basic_abstract.dart
abstract class Shape {
  // Abstract method (no implementation)
  double area();
  
  // Concrete method
  void describe() {
    print('This shape has an area of ${area()}');
  }
}

class Circle extends Shape {
  final double radius;
  
  Circle(this.radius);
  
  @override
  double area() => 3.14159 * radius * radius;
}

class Square extends Shape {
  final double side;
  
  Square(this.side);
  
  @override
  double area() => side * side;
}

void main() {
  // var shape = Shape(); // Error: Can't instantiate abstract class
  var circle = Circle(5.0);
  var square = Square(4.0);
  
  circle.describe();
  square.describe();
}

Shape 抽象类声明了一个抽象的 area 方法,子类必须实现该方法。它还提供了一个具体的 describe 方法,该方法使用抽象方法。这说明了抽象类如何混合已实现和未实现的功能。

CircleSquare 类继承自 Shape,并提供了它们对 area 的特定实现。该示例表明我们不能直接实例化 Shape - 我们必须创建具体子类的实例。

$ dart run basic_abstract.dart
This shape has an area of 78.53975
This shape has an area of 16.0

抽象类作为接口

Dart 没有单独的接口关键字 - 抽象类通常用作接口。本示例展示了如何使用抽象类来定义多个类可以不同地实现的契约。

interface_abstract.dart
abstract class PaymentProcessor {
  void processPayment(double amount);
  void refundPayment(double amount);
}

class CreditCardProcessor implements PaymentProcessor {
  @override
  void processPayment(double amount) {
    print('Processing credit card payment: \$$amount');
  }
  
  @override
  void refundPayment(double amount) {
    print('Refunding credit card payment: \$$amount');
  }
}

class PayPalProcessor implements PaymentProcessor {
  @override
  void processPayment(double amount) {
    print('Processing PayPal payment: \$$amount');
  }
  
  @override
  void refundPayment(double amount) {
    print('Refunding PayPal payment: \$$amount');
  }
}

void processOrder(PaymentProcessor processor, double amount) {
  processor.processPayment(amount);
}

void main() {
  var creditCard = CreditCardProcessor();
  var paypal = PayPalProcessor();
  
  processOrder(creditCard, 100.0);
  processOrder(paypal, 50.0);
}

PaymentProcessor 抽象类定义了一个支付处理器必须实现的接口。与使用 extends 不同,使用 implements 要求子类提供所有方法的实现,包括抽象类的具体方法。

该示例演示了多态性 - processOrder 函数接受任何 PaymentProcessor 实现。这种模式对于创建可插入式架构非常有用,因为实现可以轻松替换。

$ dart run interface_abstract.dart
Processing credit card payment: $100.0
Processing PayPal payment: $50.0

带属性的抽象类

抽象类可以定义子类必须实现的抽象属性。本示例显示了一个同时具有抽象属性和具体属性的抽象类。

properties_abstract.dart
abstract class Vehicle {
  // Abstract property
  String get name;
  
  // Concrete property
  int wheels = 4;
  
  // Abstract method
  void move();
  
  // Concrete method using abstract members
  void describe() {
    print('$name with $wheels wheels is moving');
    move();
  }
}

class Car extends Vehicle {
  @override
  final String name;
  
  Car(this.name);
  
  @override
  void move() {
    print('Vroom vroom!');
  }
}

class Bicycle extends Vehicle {
  @override
  final String name = 'Bicycle';
  
  @override
  int wheels = 2;
  
  @override
  void move() {
    print('Pedaling...');
  }
}

void main() {
  var car = Car('Tesla');
  var bike = Bicycle();
  
  car.describe();
  bike.describe();
}

Vehicle 抽象类声明了一个抽象的 name 属性和 move 方法,同时提供了默认的 wheels 值和具体的 describe 方法。CarBicycle 类以不同的方式实现这些。

这演示了抽象类如何定义接口的必需和可选方面。wheels 属性展示了具体子类如何覆盖抽象类的默认值。

$ dart run properties_abstract.dart
Tesla with 4 wheels is moving
Vroom vroom!
Bicycle with 2 wheels is moving
Pedaling...

带 Mixin 的多重继承

Dart 的 Mixin 可以与抽象类一起使用,以实现类似多重继承的行为。本示例将抽象类与 mixin 结合起来,以创建灵活的类层次结构。

mixin_abstract.dart
abstract class Animal {
  String get name;
  void makeSound();
}

mixin Walker {
  void walk() {
    print('Walking...');
  }
}

mixin Swimmer {
  void swim() {
    print('Swimming...');
  }
}

class Dog extends Animal with Walker {
  @override
  final String name;
  
  Dog(this.name);
  
  @override
  void makeSound() => print('Woof!');
}

class Duck extends Animal with Walker, Swimmer {
  @override
  final String name;
  
  Duck(this.name);
  
  @override
  void makeSound() => print('Quack!');
}

void main() {
  var dog = Dog('Buddy');
  var duck = Duck('Donald');
  
  dog.makeSound();
  dog.walk();
  
  duck.makeSound();
  duck.walk();
  duck.swim();
  
  // Can't instantiate abstract class
  // var animal = Animal();
}

Animal 抽象类定义了核心的动物行为,而 WalkerSwimmer mixin 则添加了特定的功能。DogDuck 类以不同的方式组合了这些功能。

这种模式允许灵活的代码重用,同时保持清晰的类层次结构。抽象类确保满足某些契约,而 mixin 提供模块化行为,可以根据需要混合使用。

$ dart run mixin_abstract.dart
Woof!
Walking...
Quack!
Walking...
Swimming...

抽象类中的工厂构造函数

抽象类可以具有返回具体子类实例的工厂构造函数。这对于创建对象而不暴露具体实现很有用。

factory_abstract.dart
abstract class Logger {
  void log(String message);
  
  factory Logger(String type) {
    switch (type.toLowerCase()) {
      case 'console':
        return ConsoleLogger();
      case 'file':
        return FileLogger();
      default:
        throw ArgumentError('Unknown logger type: $type');
    }
  }
}

class ConsoleLogger implements Logger {
  @override
  void log(String message) {
    print('CONSOLE: $message');
  }
}

class FileLogger implements Logger {
  @override
  void log(String message) {
    print('FILE: $message (written to log file)');
  }
}

void main() {
  var consoleLogger = Logger('console');
  var fileLogger = Logger('file');
  
  consoleLogger.log('This is a console message');
  fileLogger.log('This should go to a file');
  
  try {
    var unknownLogger = Logger('database');
  } catch (e) {
    print('Error: $e');
  }
}

Logger 抽象类定义了一个工厂构造函数,该构造函数根据输入参数返回不同的日志记录器实现。这会将具体类隐藏起来,使消费者只能与抽象接口进行交互。

这种模式对于创建在运行时需要确定确切实现的对象的很有用。它提供了一种封装对象创建逻辑的干净方法,同时为客户端维护简单的接口。

$ dart run factory_abstract.dart
CONSOLE: This is a console message
FILE: This should go to a file (written to log file)
Error: ArgumentError: Unknown logger type: database

来源

Dart 抽象类
Dart Mixins
Dart 工厂构造函数

抽象类是 Dart 中创建灵活、可维护类层次结构的强大工具。它们能够实现多态、代码重用和清晰的接口定义。通过将抽象类与 mixin 和工厂构造函数结合使用,您可以构建复杂而简洁的面向对象设计。

作者

我叫 Jan Bodnar,是一名热情的程序员,拥有丰富的编程经验。我从 2007 年开始撰写编程文章。到目前为止,我已撰写了 1400 多篇文章和 8 本电子书。我在编程教学方面拥有十多年的经验。

列出 所有 Dart 教程