在 Dart 中,抽象类和接口是实现多态和定义契约的重要机制。它们允许我们定义一个类的行为规范,而不必提供具体的实现,从而实现代码的解耦和扩展性。
1. 抽象类 (Abstract Classes)
抽象类是不能被直接实例化的类。它通常包含抽象方法(没有具体实现的方法)和非抽象方法(有具体实现的方法)。抽象类的主要目的是作为其他类的基类,提供一个通用的接口和部分实现。
abstract 关键字定义抽象类和抽象方法。// 抽象类 Shape
abstract class Shape {
// 抽象方法:计算面积,子类必须实现
double getArea();
// 非抽象方法:打印信息
void printInfo() {
print("This is a shape.");
}
}
// Circle 类继承 Shape
class Circle extends Shape {
double radius;
Circle(this.radius);
@override
double getArea() {
return 3.14159 * radius * radius;
}
@override
void printInfo() {
super.printInfo(); // 调用父类的非抽象方法
print("This is a circle with radius $radius.");
}
}
// Rectangle 类继承 Shape
class Rectangle extends Shape {
double width;
double height;
Rectangle(this.width, this.height);
@override
double getArea() {
return width * height;
}
}
void main() {
// Shape shape = Shape(); // 错误:抽象类不能直接实例化
var circle = Circle(5.0);
print("Circle Area: ${circle.getArea()}"); // 输出: Circle Area: 78.53975
circle.printInfo(); // 输出: This is a shape.
// This is a circle with radius 5.0.
var rectangle = Rectangle(4.0, 6.0);
print("Rectangle Area: ${rectangle.getArea()}"); // 输出: Rectangle Area: 24.0
}
2. 接口 (Interfaces)
在 Dart 中,没有专门的 interface 关键字。所有的类都隐式地定义了一个接口。这意味着任何类都可以作为接口被其他类实现。当一个类实现一个接口时,它必须实现该接口中定义的所有方法和属性。
implements 关键字来实现接口。// 定义一个行为接口 (隐式接口)
class Flyable {
void fly() {
print("I can fly!");
}
}
class Swimmable {
void swim() {
print("I can swim!");
}
}
// Bird 类实现 Flyable 接口
class Bird implements Flyable {
String name;
Bird(this.name);
@override
void fly() {
print("$name is flying high.");
}
}
// Duck 类实现 Flyable 和 Swimmable 接口
class Duck implements Flyable, Swimmable {
String name;
Duck(this.name);
@override
void fly() {
print("$name is flying low.");
}
@override
void swim() {
print("$name is swimming in the pond.");
}
}
void main() {
var bird = Bird("Eagle");
bird.fly(); // 输出: Eagle is flying high.
var duck = Duck("Donald");
duck.fly(); // 输出: Donald is flying low.
duck.swim(); // 输出: Donald is swimming in the pond.
}
抽象类与接口的区别:
| 特性 | 抽象类 (Abstract Class) | 接口 (Implicit Interface) |
|---|---|---|
| 关键字 | abstract |
implements (所有类都是隐式接口) |
| 实例化 | 不能直接实例化 | 不能直接实例化 (作为接口时) |
| 继承数量 | 单继承 (一个类只能 extends 一个抽象类) |
多实现 (一个类可以 implements 多个接口) |
| 成员 | 可以有抽象方法和非抽象方法、实例变量、构造函数 | 只能定义方法签名和属性签名 (没有具体实现) |
| 目的 | 定义通用行为和部分实现,作为基类 | 定义行为契约,强制实现特定方法 |
| 关系 | “is-a” 关系 (子类是父类的一种) | “can-do” 关系 (类可以做接口定义的事情) |
抽象类和接口在 Flutter 框架中被广泛使用,它们是构建可扩展、可测试和模块化应用的关键。例如,Flutter 中的 Widget 类本身就是抽象的,StatelessWidget 和 StatefulWidget 都是其子类。许多设计模式,如策略模式、工厂模式等,都依赖于抽象类和接口来实现。
案例:一个主题切换器,演示抽象类和接口的应用
我们将创建一个简单的应用,允许用户切换不同的主题(亮色/暗色)。这个案例将使用抽象类定义主题的通用接口,并使用具体类实现不同的主题,从而演示抽象类和接口在 Flutter 主题管理中的应用。
import 'package:flutter/material.dart';
// 抽象类:定义主题的通用接口
abstract class AppTheme {
String get themeName;
Color get primaryColor;
Color get accentColor;
Color get textColor;
Color get backgroundColor;
// 抽象方法,子类实现具体的主题样式
ThemeData getThemeData();
}
// 亮色主题实现
class LightTheme implements AppTheme {
@override
String get themeName => '亮色主题';
@override
Color get primaryColor => Colors.blue;
@override
Color get accentColor => Colors.lightBlueAccent;
@override
Color get textColor => Colors.black87;
@override
Color get backgroundColor => Colors.white;
@override
ThemeData getThemeData() {
return ThemeData(
brightness: Brightness.light,
primaryColor: primaryColor,
colorScheme: ColorScheme.light(
primary: primaryColor,
secondary: accentColor,
),
scaffoldBackgroundColor: backgroundColor,
textTheme: TextTheme(
bodyMedium: TextStyle(color: textColor),
),
appBarTheme: AppBarTheme(
backgroundColor: primaryColor,
foregroundColor: Colors.white,
),
);
}
}
// 暗色主题实现
class DarkTheme implements AppTheme {
@override
String get themeName => '暗色主题';
@override
Color get primaryColor => Colors.deepPurple;
@override
Color get accentColor => Colors.purpleAccent;
@override
Color get textColor => Colors.white70;
@override
Color get backgroundColor => Colors.grey[900]!;
@override
ThemeData getThemeData() {
return ThemeData(
brightness: Brightness.dark,
primaryColor: primaryColor,
colorScheme: ColorScheme.dark(
primary: primaryColor,
secondary: accentColor,
),
scaffoldBackgroundColor: backgroundColor,
textTheme: TextTheme(
bodyMedium: TextStyle(color: textColor),
),
appBarTheme: AppBarTheme(
backgroundColor: primaryColor,
foregroundColor: Colors.white,
),
);
}
}
class ThemeSwitcherApp extends StatefulWidget {
const ThemeSwitcherApp({super.key});
@override
State<ThemeSwitcherApp> createState() => _ThemeSwitcherAppState();
}
class _ThemeSwitcherAppState extends State<ThemeSwitcherApp> {
AppTheme _currentTheme = LightTheme(); // 初始主题为亮色主题
void _toggleTheme() {
setState(() {
// 根据当前主题切换到另一个主题
if (_currentTheme is LightTheme) {
_currentTheme = DarkTheme();
} else {
_currentTheme = LightTheme();
}
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: _currentTheme.getThemeData(), // 应用当前主题
home: Scaffold(
appBar: AppBar(
title: Text(_currentTheme.themeName), // 显示当前主题名称
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Hello, Flutter!',
style: TextStyle(
fontSize: 30,
color: _currentTheme.textColor, // 使用当前主题的文本颜色
),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: _toggleTheme,
child: const Text('切换主题'),
),
],
),
),
),
);
}
}
void main() {
runApp(const ThemeSwitcherApp());
}
案例分析:
abstract class AppTheme:我们定义了一个抽象类 AppTheme,它定义了所有主题都应该具有的属性(如 themeName、primaryColor 等)和抽象方法 getThemeData()。这个抽象类充当了一个契约,规定了所有具体主题类必须实现的行为。class LightTheme implements AppTheme 和 class DarkTheme implements AppTheme:LightTheme 和 DarkTheme 分别实现了 AppTheme 接口。它们提供了 AppTheme 中所有属性和抽象方法的具体实现,定义了亮色和暗色主题的视觉样式。_currentTheme = LightTheme();:在 _ThemeSwitcherAppState 中,_currentTheme 变量的类型是 AppTheme。这体现了多态性,因为 _currentTheme 可以引用 LightTheme 或 DarkTheme 的实例。_currentTheme.getThemeData():在 MaterialApp 的 theme 属性中,我们调用了 _currentTheme.getThemeData()。由于多态性,Dart 会根据 _currentTheme 实际引用的对象类型(LightTheme 或 DarkTheme)来调用相应的 getThemeData() 方法,从而应用不同的主题。if (_currentTheme is LightTheme):在 _toggleTheme 方法中,我们使用 is 关键字来判断 _currentTheme 的实际类型,从而决定切换到哪个主题。这展示了类型测试运算符在运行时判断对象类型的作用。这个案例清晰地展示了 Dart 的抽象类和接口在 Flutter 应用中的实际应用。通过定义抽象接口,我们可以实现主题逻辑与 UI 渲染的解耦,使得添加新的主题变得非常容易,并且代码结构更加清晰和可维护。这种设计模式在 Flutter 中非常常见,用于构建灵活和可扩展的组件。