Menu
CoddyTech

类中的空安全

本课属于 Coddy Dart 学习旅程的“面向对象编程”单元。第 28 课,共 110 课。

设计类时,你需要决定哪些字段是必需的,哪些是可选的。空安全会影响你声明实例变量和编写构造函数的方式。

非空字段必须在构造函数体运行之前完成初始化。你可以通过初始化列表、构造函数参数或默认值来完成:

class User {
  String name;           // 必须初始化
  String? bio;           // 可选,可以为 null
  int age = 0;           // 有默认值
  
  User(this.name);       // 通过参数初始化 name
}

对于可空字段,你可以不初始化它们,因为它们默认值为 null。这适用于可选数据:

class Product {
  String title;
  String? description;   // 可选的商品描述
  double? discount;      // 默认没有折扣
  
  Product(this.title);
}

var item = Product('Laptop');
print(item.description);  // null
print(item.discount);     // null

访问可空字段时,请使用你学过的空值感知运算符:

class Order {
  String id;
  User? customer;
  
  Order(this.id);
  
  String getCustomerName() {
    return customer?.name ?? 'Guest';
  }
}

命名参数中的 required 关键字可确保调用者提供必要的值:

class Account {
  String email;
  String? phone;
  
  Account({required this.email, this.phone});
}

var acc = Account(email: 'test@mail.com');  // phone 是可选的

通过认真选择可空字段和不可空字段,你可以创建清晰表达哪些数据是必需的、哪些是可选的类。

challenge icon

挑战

简单

我们来为一家酒店构建一个预订系统,展示如何在空安全中合理搭配必填字段和可选字段来设计类。

你要把代码整理到两个文件中:

  • reservation.dart:定义一个表示酒店预订的 Reservation 类。每笔预订都需要一些必要信息,但有些详情是可选项,客人可能会提供,也可能不会提供。你的类应包含:
    • 一个不可空的 String guestName,每笔预订都必须有客人姓名
    • 一个不可空的 int roomNumber,每笔预订都必须分配房间
    • 一个不可空的 int nights,默认值为 1,最短住宿时间为一晚
    • 一个可空的 String? specialRequests,客人可能会有特殊要求
    • 一个可空的 DateTime? checkInTime,可能无法确定确切的到达时间
    • 一个使用命名参数的构造函数,其中 guestName 和 roomNumber 使用 required,而 nights 为可选参数,并设有默认值
    • 一个名为 getRequestsSummary() 的方法,如果提供了特殊要求,就返回这些要求;否则使用 ?? 运算符返回 'No special requests'
    • 一个名为 getArrivalInfo() 的方法,使用 ?. 返回入住时间的字符串表示;如果未设置,则返回 null
    • 一个名为 printConfirmation() 的方法,用于显示预订详情
  • main.dart:导入预订类,并创建预订,展示必填数据和可选数据的不同组合:
    • 为客人 'Sarah Johnson' 创建一笔预订,房间号为 101,入住 3 晚,特殊要求为 'Late checkout',入住时间设为 DateTime(2024, 6, 15, 14, 30)
    • 为客人 'Mike Chen' 创建一笔预订,房间号为 205(使用默认的住宿晚数),只设置特殊要求 'Extra pillows'
    • 为客人 'Emma Davis' 创建一笔预订,房间号为 310,入住 2 晚(两个可选字段都保持为 null)
    • 按顺序处理每笔预订,调用 printConfirmation(),然后使用 getArrivalInfo() 打印 Arrival: [result]

printConfirmation() 方法应按以下格式打印:

=== Reservation Confirmed ===
Guest: [guestName]
Room: [roomNumber]
Nights: [nights]
Requests: [getRequestsSummary()]

预期输出:

=== Reservation Confirmed ===
Guest: Sarah Johnson
Room: 101
Nights: 3
Requests: Late checkout
Arrival: 2024-06-15 14:30:00.000
=== Reservation Confirmed ===
Guest: Mike Chen
Room: 205
Nights: 1
Requests: Extra pillows
Arrival: null
=== Reservation Confirmed ===
Guest: Emma Davis
Room: 310
Nights: 2
Requests: No special requests
Arrival: null

亲手试试

import 'reservation.dart';

void main() {
  // TODO:为 'Sarah Johnson' 创建预订
  // 101 号房,住 3 晚,特殊要求:'Late checkout'
  // 入住时间:DateTime(2024, 6, 15, 14, 30)
  
  // TODO:为 'Mike Chen' 创建预订
  // 205 号房,使用默认入住晚数,特殊要求:'Extra pillows'
  // 未指定入住时间
  
  // TODO:为 'Emma Davis' 创建预订
  // 310 号房,住 2 晚,无特殊要求,未指定入住时间
  
  // TODO:按顺序对每个预订执行以下操作:
  // 1. 调用 printConfirmation()
  // 2. 使用 getArrivalInfo() 打印 'Arrival: [result]'
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: Dart 在线编译器