1.7 继承与多态

基础知识

继承(Inheritance)和多态(Polymorphism)是面向对象编程的两个核心概念,它们使得代码更具可重用性、可扩展性和灵活性。Dart 语言完全支持这两种特性。

1. 继承

继承允许一个类(子类或派生类)从另一个类(父类或基类)继承属性和方法。子类可以重用父类的代码,并可以添加新的属性和方法,或者重写(Override)父类的方法。

在 Dart 中,使用 extends 关键字来实现继承。一个类只能继承一个父类(单继承)。

dart
复制代码
// 父类
class Animal {
  String name;

  Animal(this.name);

  void eat() {
    print("$name is eating.");
  }

  void sleep() {
    print("$name is sleeping.");
  }
}

// 子类 Dog 继承自 Animal
class Dog extends Animal {
  String breed;

  Dog(String name, this.breed) : super(name); // 调用父类的构造函数

  void bark() {
    print("$name is barking.");
  }

  @override // 重写父类方法
  void eat() {
    print("$name (a $breed) is happily eating dog food.");
  }
}

// 子类 Cat 继承自 Animal
class Cat extends Animal {
  Cat(String name) : super(name);

  void meow() {
    print("$name is meowing.");
  }

  @override
  void eat() {
    print("$name (a cat) is gracefully eating fish.");
  }
}

void main() {
  var animal = Animal("Generic Animal");
  animal.eat(); // 输出: Generic Animal is eating.

  var dog = Dog("Buddy", "Golden Retriever");
  dog.eat();  // 输出: Buddy (a Golden Retriever) is happily eating dog food.
  dog.bark(); // 输出: Buddy is barking.
  dog.sleep(); // 继承自 Animal

  var cat = Cat("Whiskers");
  cat.eat();  // 输出: Whiskers (a cat) is gracefully eating fish.
  cat.meow(); // 输出: Whiskers is meowing.
}

关键点:

  • extends 关键字:用于声明继承关系。
  • super 关键字:用于调用父类的构造函数或方法。
  • @override 注解:用于标记重写父类的方法。这是一个可选的注解,但强烈建议使用,它可以帮助编译器检查是否正确重写了方法,避免拼写错误等问题。

2. 多态

多态意味着一个对象可以呈现出多种形态。在面向对象编程中,多态通常体现在以下几个方面:

  • 方法重写(Method Overriding):子类可以提供父类中已有的方法的不同实现。当通过父类引用调用该方法时,实际执行的是子类中的实现。
  • 向上转型(Upcasting):子类对象可以被当作父类对象来引用。例如,Dog 对象可以被赋值给 Animal 类型的变量。
dart
复制代码
void main() {
  // 向上转型:子类对象可以被当作父类对象来引用
  Animal myPet1 = Dog("Max", "Labrador");
  Animal myPet2 = Cat("Luna");

  // 调用 eat() 方法,实际执行的是子类中重写的方法
  myPet1.eat(); // 输出: Max (a Labrador) is happily eating dog food.
  myPet2.eat(); // 输出: Luna (a cat) is gracefully eating fish.

  // List<Animal> 存储不同类型的 Animal 子类对象
  List<Animal> pets = [Dog("Rocky", "Bulldog"), Cat("Milo"), Animal("Parrot")];

  for (var pet in pets) {
    pet.eat(); // 多态性体现:根据实际对象类型调用不同的 eat() 实现
  }
  // 输出:
  // Rocky (a Bulldog) is happily eating dog food.
  // Milo (a cat) is gracefully eating fish.
  // Parrot is eating.
}

多态性使得代码更加灵活和通用。我们可以编写处理父类类型的方法,而这些方法可以自动适应其所有子类,无需为每个子类编写单独的代码。

官方文档链接

Flutter 开发中的应用案例

继承和多态是 Flutter 框架的核心。Flutter 中的所有 Widget 都继承自 Widget 类,而 StatelessWidgetStatefulWidget 又继承自 Widget。这种继承关系和多态性使得 Flutter 的 UI 构建变得非常强大和灵活。

案例:不同类型的卡片组件

我们将创建一个基础的 BaseCard 类,然后派生出 ImageCardTextCard 两个子类,分别用于显示图片和纯文本内容。这个案例将演示继承和多态在 Flutter UI 组件设计中的应用。

dart
复制代码
import 'package:flutter/material.dart';

// 基础卡片类 (父类)
abstract class BaseCard extends StatelessWidget {
  final String title;

  const BaseCard({super.key, required this.title});

  // 抽象方法,子类必须实现
  Widget buildContent(BuildContext context);

  @override
  Widget build(BuildContext context) {
    return Card(
      margin: const EdgeInsets.all(16.0),
      elevation: 4.0,
      child: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            Text(
              title,
              style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
            ),
            const SizedBox(height: 10),
            buildContent(context), // 调用子类实现的具体内容构建方法
          ],
        ),
      ),
    );
  }
}

// 图片卡片类 (子类)
class ImageCard extends BaseCard {
  final String imageUrl;
  final String description;

  const ImageCard({
    super.key,
    required super.title,
    required this.imageUrl,
    required this.description,
  });

  @override
  Widget buildContent(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: <Widget>[
        Image.network(
          imageUrl,
          height: 150,
          width: double.infinity,
          fit: BoxFit.cover,
        ),
        const SizedBox(height: 10),
        Text(
          description,
          style: const TextStyle(fontSize: 16),
        ),
      ],
    );
  }
}

// 文本卡片类 (子类)
class TextCard extends BaseCard {
  final String content;

  const TextCard({
    super.key,
    required super.title,
    required this.content,
  });

  @override
  Widget buildContent(BuildContext context) {
    return Text(
      content,
      style: const TextStyle(fontSize: 16),
    );
  }
}

class InheritanceAndPolymorphismExampleScreen extends StatelessWidget {
  const InheritanceAndPolymorphismExampleScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("继承与多态应用案例"),
      ),
      body: ListView(
        children: <Widget>[
          // 使用 ImageCard
          ImageCard(
            title: "美丽的风景",
            imageUrl: "https://picsum.photos/id/237/200/300", // 示例图片 URL
            description: "这是一张美丽的风景图片,展示了大自然的鬼斧神工。",
          ),
          // 使用 TextCard
          TextCard(
            title: "关于 Dart 语言",
            content: "Dart 是一种客户端优化的编程语言,旨在为多平台应用开发提供高效、可预测的性能。它被设计用于构建移动、桌面、Web 和后端应用程序。",
          ),
          // 也可以将不同类型的卡片存储在同一个列表中,体现多态性
          // List<BaseCard> cards = [
          //   ImageCard(
          //     title: "另一张图片",
          //     imageUrl: "https://picsum.photos/id/238/200/300",
          //     description: "这是通过多态性展示的另一张图片卡片。",
          //   ),
          //   TextCard(
          //     title: "多态文本",
          //     content: "这段文本通过多态性展示。",
          //   ),
          // ];
          // ... 在 ListView 中遍历 cards
        ],
      ),
    );
  }
}

void main() {
  runApp(const MaterialApp(home: InheritanceAndPolymorphismExampleScreen()));
}

案例分析:

  • abstract class BaseCard extends StatelessWidget:我们定义了一个抽象类 BaseCard,它继承自 Flutter 的 StatelessWidget。抽象类不能直接实例化,它定义了一个通用的结构和行为,并强制子类实现某些方法(通过 abstract 关键字)。
  • Widget buildContent(BuildContext context);BaseCard 中定义了一个抽象方法 buildContent。这意味着任何继承 BaseCard 的子类都必须提供 buildContent 方法的具体实现。这体现了多态性,因为不同的子类会以自己的方式构建内容。
  • ImageCard extends BaseCardTextCard extends BaseCardImageCardTextCard 分别继承自 BaseCard。它们都重写了 buildContent 方法,提供了各自特定的内容布局(图片和文本)。
  • super.title:在子类的构造函数中,super.title 用于将 title 参数传递给父类 BaseCard 的构造函数。这是调用父类构造函数的方式。
  • buildContent(context):在 BaseCardbuild 方法中,我们调用了 buildContent(context)。由于多态性,当 BaseCard 的实例实际上是 ImageCardTextCard 时,会调用相应子类中重写的 buildContent 方法,从而显示不同的内容。
  • ListView 中的组合:在 InheritanceAndPolymorphismExampleScreen 中,我们直接在 ListViewchildren 中使用了 ImageCardTextCard 的实例。尽管它们是不同的类,但由于它们都继承自 BaseCard(最终都继承自 Widget),它们可以被统一处理和渲染。

这个案例清晰地展示了继承和多态在 Flutter UI 组件设计中的强大作用。通过继承,我们可以创建可复用的基础组件,并通过多态性实现组件行为的定制化,从而构建出灵活且易于维护的 UI 结构。Flutter 框架本身就是这种设计模式的典范。