likes
comments
collection
share

Dart 3.0非常实用的新特性和你为什么要关心

作者站长头像
站长
· 阅读数 16

介绍

我看过几篇相当冗长的文章,所以我想我会简要介绍 Dart 即将推出的最佳新功能以及您应该关心的原因(按照我的兴奋顺序)。您应该能够将每个示例复制并粘贴到DartPad中进行操作。要使用 Dart 3.0,请将Flutter 升级到 3.10.0

本文简要总结了以下内容:

  • 密封类
  • 记录(元组)
  • Widget中的switch
  • 解构
  • 新列表扩展
  • 新的类修饰符(以及它们到底是如何工作的)

这并没有涵盖所有内容,因此有关所有详细信息,请查看:

密封类

  • 它们是什么: 一个类修饰符,编译器确保该类型的值的开关详尽地涵盖每个子类型,并为子类型字段提供编译时保证
  • 为什么你应该关心: 它们特别适合在你的层之间传递状态(data➡️presentation & presentation️️️➡️view)。每个状态都可以有自己的(不同的)编译时安全的属性

此示例显示如何包装响应类型并在没有检查实例的情况下安全地访问data或error。

比如:你对网络请求的响应进行解析封装

sealed class Response{}

class Success<Type> extends Response {
  final Type data;
  Success(this.data);
}

class Failure extends Response {
  final Error error;
  Failure(this.error);
}

String toString(Response response) => switch (response) {
      // Can access `response.data` without an instance check!
      Success _ => response.data.toString(),
      Failure _ => response.error.toString(),
    };
String toTypeString(Response response) => switch (response) {
      Success _ => 'Success',
      Failure _ => 'Failure',
    };

记录(又名 n 元组)

  • 它们是什么: 允许您从一个函数返回多个值/分配给一个变量
  • 为什么你应该关心: 它们是一个很好的速记,让你不需要写一个完整的类。它对于简单的东西(如纬度/经度和 x/y)可能非常有用。但也适用于涉及线程的更复杂的用例。
// Function return
(double lat, double lon) geoLocation(String name) =>
    (231.23, 36.8219);

void examples() {
  // Variable declaration / assignment
  (String, int) record;
  record = ('A string', 123);

  // Named-args
  ({int a, bool b}) record;
  record = (a: 123, b: true);

  // Accessing them!
  var record = ('first', a: 2, b: true, 'last');
  print(record.$1); // Prints 'first'
  print(record.a); // Prints 2
  print(record.b); // Prints true
  print(record.$2); // Prints 'last'
}

在小部件内部切换(而不是三元和 if()…[]

  • 它们是什么: 在不允许语句的上下文中使用模式和多路分支
  • 为什么要关心: 在 Flutter 中使用 if/elses(或链式三元组)build非常尴尬。现在您可以使用一个 switch 语句,它的行为类似于 if,使用新的模式匹配语法可以让您做一些很酷的事情,因为它_是通配符匹配。
switch (state) {
  _ when state == 'loading' => const Text('Loading...'),
  _ when state == 'content' => const Text('My content widget goes here'),
  // Else case
  _ => const Text('Unknown state encountered'),
},

return TextButton(
  onPressed: _goPrevious,
  child: Text(switch (page) {
    0 => 'Exit story',
    1 => 'First page',
    _ when page == _lastPage => 'Start over',
    _ => 'Previous page',
  }),
);

通过模式匹配解构

  • 它们是什么: 从类、记录和列表中提取属性并将它们分配给各个变量的速记
  • 为什么你应该关心: 你可能会想要传递整个对象,因为访问它的每个属性都比较冗长。现在您可以一次分配多个变量。
class Person {
  String name;
  int age;
  Person({this.name = 'John', this.age = 30});
}

void examples() {
  // Records
  final (lat, lon) = geoLocation('Nairobi');
  // Class
  final Person person = Person(name: 'John', age: 30);
  final Person(:name, :age) = person;
  print('Name $name, age $age');
  // Lists
  var numList = [1, 2, 3];
  var [a, b, c] = numList;
}

新列表扩展

  • 它们是什么: 您可以在列表中调用的辅助函数
  • 为什么要关心: 尝试访问第一个、最后一个或查找元素时不再有索引越界问题!

nonNulls、firstOrNull、lastOrNull、singleOrNull、elementAtOrNull 并在 Iterables 上建立索引。

final list = []
// Do this
final lastElement = list.lastOrNull()
// Instead of this
final lastElement = list[list.length - 1]

类修饰符

TL;DR:用于abstract interface传统的interface. 用于abstract传统的抽象类。final如果它们不应该被覆盖,则用于常规类。

此表是完整规格表的简化版本

Dart 3.0非常实用的新特性和你为什么要关心

我已经发现 Dart 中的类修饰符很奇怪,现在它们更奇怪了(但以一种强大的方式!)。我将只介绍新内容。

您需要了解的第一件事是和之间的区别。这些概念存在于 Dart 2.x 中,但得到了扩展。implements,extends

  • implements= 您的子类与父类具有完全相同的“形状”(字段/函数),但必须定义它自己的所有行为。您不会从父函数或字段继承任何功能。它与其他语言类似,但您implement几乎可以使用任何类(无论修饰符如何)
  • extends= 你的子类可以扩展(覆盖)行为、继承行为和实现行为

最终的

  • 它是什么: 您不能在其定义的文件之外扩展或实现该类
  • 为什么要关心:这是封装概念的核心。通过防止类被子类化,可以确保类的行为不会以意想不到的方式改变,并且可以隐藏类的内部结构。
// -- File a.dart
final class FinalClass {}

// -- File b.dart
// Not allowed
class ExtensionClass extends FinalClass{}
// Not allowed
class ExtensionClass implements FinalClass{}

抽象接口

  • 它是什么: 更像是一个传统的界面。只能实施(不能扩展)。但是你可以定义没有主体的函数。
  • 为什么你应该关心:你可以只定义“形状”而不定义任何功能。父类中没有隐藏任何内容。
// -- File a.dart
interface class InterfaceClass {
  String name = 'Dave'; // Allowed
  void body() { print('body'); } // Allowed

  int get myField; // Not allowed
  void noBody(); // Not allowed
}

// -- File b.dart
// Not allowed
class ExtensionClass extends InterfaceClass{}
// Allowed
class ConcreteClass implements InterfaceClass{
  // Have to override everything
  @override
  String name = 'ConcreteName';
  @override
  void function() { print('body'); }
}

抽象类

  • 它是什么: 可以实施和扩展。可以拥有具有子级继承的功能的主体,或者没有子级必须实现的主体。
  • 为什么你应该关心:你可以构建非常强大的类层次结构来定义可重用的逻辑位。但这也伴随着某些行为的不可预测性的诅咒(基于你在层次结构中的位置)
// -- File a.dart
abstract interface class AbstractInterfaceClass {
  String name = 'Dave'; // Allowed
  void body() { print('body'); } // Allowed
  
  // This is a more traditional implementation
  int get myField; // Allowed
  void noBody(); // Allowed
}

// -- File b.dart
// Not allowed
class ExtensionClass extends AbstractInterfaceClass{}
// Allowed
class ConcreteClass implements InterfaceClass{
  // Have to override everything
  @override
  String name = 'ConcreteName';
  @override
  void function() { print('body'); }

  @override
  int get myField => 5
  @override
  void noBody() = print('concreteBody')
}