【Dart 语言学习教程 】 第二章:面向对象编程

本章深入 Dart 的面向对象特性,涵盖类与对象、构造函数、私有成员、继承、抽象类、混入、枚举等核心概念。每个知识点配有完整代码示例和详细解析,帮助掌握 Dart 独特的 OOP 风格。


2.1 类与对象

知识点

  • 类的定义 :使用 class 关键字
  • 成员变量:实例变量(可设置初始值)
  • 成员方法:定义对象行为
  • 对象创建 :new 关键字可选,通常省略
  • this 关键字:指向当前实例,用于区分参数和成员变量

代码示例

dart 复制代码
void main() {
  // 创建对象
  var person = Person('Alice', 25);
  person.sayHello();

  // 修改属性
  person.age = 26;
  person.sayHello();
}

class Person {
  // 成员变量
  String name;
  int age;

  // 构造函数
  Person(this.name, this.age);

  // 成员方法
  void sayHello() {
    print('Hello, I am $name, $age years old.');
  }
}

解析

  • Person(this.name, this.age) 是 Dart 的语法糖,自动将参数赋值给同名成员变量。
  • 实例变量默认是 public,没有 public / private 关键字(使用 _ 前缀实现私有)。
  • Dart 中所有对象都继承自 Object 类。

2.2 构造函数

知识点

  • 默认构造函数:与类同名,无参数
  • 命名构造函数 :类名.名称(),用于多种构造方式
  • 初始化列表:构造函数体执行前初始化成员变量
  • 常量构造函数 :const 关键字,用于创建编译时常量对象
  • 工厂构造函数 :factory,用于控制实例的返回(如单例、缓存)

代码示例

dart 复制代码
void main() {
  // 普通构造
  var p1 = Point(1, 2);

  // 命名构造
  var origin = Point.origin();

  // 常量构造
  const immutable = ImmutablePoint(3, 4);

  // 工厂构造(单例)
  var instance1 = Logger('main');
  var instance2 = Logger('main');
  print(identical(instance1, instance2)); // true(相同实例)
}

class Point {
  double x, y;
  // 普通构造函数 + 初始化列表
  Point(this.x, this.y);

  // 命名构造函数
  Point.origin() : x = 0, y = 0;

  // 重定向构造函数(调用另一个构造函数)
  Point.fromJson(Map<String, double> json) : this(json['x']!, json['y']!);
}

class ImmutablePoint {
  final double x, y;
  // 常量构造函数(所有成员必须 final)
  const ImmutablePoint(this.x, this.y);
}

class Logger {
  final String name;
  static final Map<String, Logger> _cache = {};

  // 工厂构造函数:返回缓存实例
  factory Logger(String name) {
    return _cache.putIfAbsent(name, () => Logger._internal(name));
  }

  // 私有命名构造,供工厂使用
  Logger._internal(this.name);
}

解析

  • 初始化列表在构造函数体之前执行,适合初始化 final 变量。
  • 常量构造函数必须用 const 调用,可放在常量上下文中,Flutter 中能提升性能。
  • 工厂构造函数不创建新实例,可以返回已有对象或子类型。

2.3 私有成员

知识点

  • 私有性 :使用 _ 前缀标识符为库私有
  • 范围 :私有成员只能在当前 .dart 文件内访问(库级别)
  • Getter / Setter:提供对私有字段的受控访问

代码示例

dart 复制代码
void main() {
  var bank = BankAccount('12345');
  bank.deposit(100);
  print(bank.balance); // 100
  // bank._balance = 500;  // 错误:私有成员不可访问
  // bank._validate();      // 错误
}

class BankAccount {
  String accountId;
  double _balance;        // 私有变量

  BankAccount(this.accountId) : _balance = 0;

  // Getter
  double get balance => _balance;

  // Setter(示例,通常不直接暴露 setter)
  set balance(double value) {
    if (value >= 0) _balance = value;
  }

  void deposit(double amount) {
    if (amount > 0) {
      _balance += amount;
      _validate();
    }
  }

  void _validate() {      // 私有方法
    if (_balance < 0) throw Exception('Negative balance');
  }
}

解析

  • Dart 没有 private 关键字,以 _ 开头即表示私有。
  • 私有成员仍可通过反射访问,但不推荐。
  • Getter 和 Setter 看起来像普通属性,但实际是方法调用。

2.4 Getter / Setter

知识点

  • 计算属性 :通过 get 关键字定义没有存储空间的值
  • 自定义 Setter :通过 set 关键字拦截赋值
  • 使用方式 :像普通字段一样使用 . 访问

代码示例

dart 复制代码
void main() {
  var rect = Rectangle(3, 4);
  print(rect.area);       // 12(计算属性)
  rect.area = 20;         // 调用 setter,修改宽高
  print(rect.width);      // 4.0
  print(rect.height);     // 5.0
}

class Rectangle {
  double width, height;

  Rectangle(this.width, this.height);

  // Getter
  double get area => width * height;

  // Setter:设置面积时按比例调整宽高
  set area(double value) {
    var ratio = width / height;
    width = (value * ratio).sqrt();
    height = width / ratio;
  }
}

解析

  • Getter 没有参数,必须有返回值。
  • Setter 只能有一个参数,没有返回值。
  • 实际开发中常使用 Getter 暴露私有字段的只读版本。

2.5 继承

知识点

  • extends:单继承一个父类
  • @override:标注重写父类方法(可选但推荐)
  • super:调用父类的构造函数或方法
  • 方法重写:子类可以改变父类行为

代码示例

dart 复制代码
void main() {
  var dog = Dog('Buddy');
  dog.speak();    // Buddy says Woof!
  dog.eat();      // Buddy is eating.

  var animal = Animal('Generic');
  animal.speak(); // Generic makes sound.
}

class Animal {
  String name;
  Animal(this.name);

  void speak() {
    print('$name makes sound.');
  }

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

class Dog extends Animal {
  Dog(String name) : super(name);

  @override
  void speak() {
    print('$name says Woof!');
  }

  // 可以选择不重写 eat,直接继承
}

解析

  • 子类构造函数必须调用父类构造函数(默认调用无参构造,有参需手动 super(...))。
  • @override 不是必需的,但有助于编译器检查和代码可读性。
  • Dart 不支持多继承(但可以通过混入实现类似效果)。

2.6 抽象类与接口

知识点

  • 抽象类 :abstract 关键字,不能实例化,可包含抽象方法(无方法体)
  • 接口 :每个类都隐含是一个接口,使用 implements 实现多个接口
  • 区别 :extends 继承实现;implements 只继承接口签名,必须重新实现所有方法

代码示例

dart 复制代码
void main() {
  // Animal a = Animal(); // 错误:抽象类不能实例化
  Dog dog = Dog();
  dog.speak();
  dog.run();

  // 使用接口实现
  Robot robot = Robot();
  robot.move();
}

abstract class Animal {
  void speak();   // 抽象方法,无方法体
  void breathe() {
    print('Breathing');
  }
}

class Dog implements Animal {
  // 必须实现所有抽象方法
  @override
  void speak() => print('Woof!');

  // 即使父类有实现,implements 后也要重新实现
  @override
  void breathe() => print('Dog breathing');

  void run() => print('Running');
}

// 接口示例:定义一个移动接口
abstract class Movable {
  void move();
}

class Robot implements Movable {
  @override
  void move() => print('Robot moving');
}

解析

  • 抽象类可以包含具体方法,子类通过 extends 继承。
  • 一个类可以 implements 多个接口,用逗号分隔。
  • 推荐使用 abstract class 定义纯接口,或使用 mixin。

2.7 混入(Mixin)

知识点

  • mixin:定义可复用的代码片段,不含构造函数
  • with:在类中使用混入,可组合多个
  • on:限制混入只能用于特定类型的子类

代码示例

dart 复制代码
void main() {
  var bird = Bird();
  bird.fly();   // Flying
  bird.swim();  // Swimming

  var fish = Fish();
  fish.swim();  // Swimming
  // fish.fly(); // 错误:Fish 没有 fly
}

// 定义混入
mixin Flyable {
  void fly() => print('Flying');
}

mixin Swimmable {
  void swim() => print('Swimming');
}

// 使用 with 组合混入
class Bird with Flyable, Swimmable {}

class Fish with Swimmable {}

// 限制混入作用域
mixin Walker on Animal {
  void walk() => print('Walking');
}

class Animal {}

class Dog extends Animal with Walker {}  // 合法,Walker 要求 on Animal
// class Table with Walker {}  // 错误:Table 不是 Animal 的子类

解析

  • 混入可以替代多重继承,解决代码复用问题。
  • 如果一个混入使用了 on,则只能被 on 指定类型的子类使用。
  • 混入不能有构造函数。

2.8 枚举

知识点

  • 基本枚举 :enum 关键字,列出常量值
  • 扩展枚举:Dart 2.17+ 支持字段、方法、构造函数
  • 访问方式 :EnumName.value,可获取 index 和 name

代码示例

dart 复制代码
void main() {
  // 基本枚举
  var color = Color.red;
  print(color.index);   // 0
  print(color.name);    // 'red'

  switch (color) {
    case Color.red:
      print('红色');
      break;
    case Color.green:
      print('绿色');
      break;
    case Color.blue:
      print('蓝色');
      break;
  }

  // 扩展枚举
  print(Status.success.message);
  print(Status.pending.displayName);

  // 遍历枚举值
  for (var status in Status.values) {
    print(status);
  }
}

// 基本枚举
enum Color { red, green, blue }

// 增强枚举(Dart 2.17+)
enum Status {
  pending('等待中', 0),
  success('成功', 1),
  error('失败', -1);

  final String displayName;
  final int code;

  const Status(this.displayName, this.code);

  String get message => displayName;
}

解析

  • 枚举可用于表示一组固定的常量。
  • 增强枚举可以拥有实例变量、构造函数(必须是 const)和方法。
  • Enum.values 可以获取所有枚举值的列表。

总结

第二章全面覆盖了 Dart 面向对象编程的核心概念,包括类、构造函数、私有成员、继承、抽象类/接口、混入以及枚举。这些知识是构建 Flutter 应用的基石,熟练掌握后可以编写出结构清晰、可复用的代码。下一章将进入 函数式编程与高阶特性。

相关推荐
每天题库2 分钟前
登高架设作业考试题库大全 核心考点真题刷题备考通关秘籍
学习·安全·考试·题库·考证
andrsted24 分钟前
练题簿在线免费刷题:听读练题支持暂停续播、循环播放,让自己的题库听得更方便
学习·微信小程序·小程序·刷题·在线刷题
李日华大战鸡红12 小时前
FOC状态空间方程模型推导(学习记录)
python·学习·线性代数
kkkkkkkkkk_Z13 小时前
新手自学嵌入式 | 学习日记:异常与中断基础、i.MX6ULL裸机开发环境搭建、GPIO模块与点灯原理
学习
m4Rk_14 小时前
【论文阅读】Agent 记忆机制(94):MemGen——在推理过程中动态生成并织入潜在记忆
论文阅读·人工智能·学习·开源·github
晓德14 小时前
0、LLM大模型安全学习系列(启)
学习·安全
Titan202415 小时前
MySQL索引学习笔记
笔记·学习·mysql
JWASX15 小时前
Java 转 go 学习 - kitex(5)
学习·golang
AOI小白新手上路16 小时前
VDMamba·AutoDL 复现笔记 · 学习笔记
笔记·学习
~kiss~16 小时前
LangGraph ~ 为图添加跨线程持久化
学习