15

我已将我的 Dart 代码迁移到 NNBD / Null Safety。其中一些看起来像这样:

class Foo {
  String? _a;
  void foo() {
    if (_a != null) {
      _a += 'a';
    }
  }
}

class Bar {
  Bar() {
    _a = 'a';
  }
  String _a;
}

这会导致两个分析错误。对于_a += 'a';

值可以为 'null' 的表达式必须先进行 null 检查,然后才能取消引用。在取消引用之前尝试检查该值是否为“null”。

对于Bar() {

必须初始化不可为空的实例字段“_a”。尝试添加初始化表达式,或在此构造函数中添加字段初始化程序,或将其标记为“迟到”。

在这两种情况下,我已经完全按照错误提示进行了操作!那是怎么回事?

我正在使用 Dart 2.12.0-133.2.beta(12 月 15 日星期二)。

编辑:我发现这个页面说:

分析器无法对整个应用程序的流程进行建模,因此它无法预测全局变量或类字段的值。

但这对我来说没有意义——在这种情况下只有一个可能的流控制路径if (_a != null)——没有异步代码,而且 Dart 是单线程的——所以不是本地的_a += 'a';也没关系。_a

并且错误消息Bar()明确说明了在构造函数中初始化字段的可能性。

4

4 回答 4

21

问题是类字段可以被覆盖,即使它被标记为final. 下面的例子说明了这个问题:

class A {
  final String? text = 'hello';

  String? getText() {
    if (text != null) {
      return text;
    } else {
      return 'WAS NULL!';
    }
  }
}

class B extends A {
  bool first = true;

  @override
  String? get text {
    if (first) {
      first = false;
      return 'world';
    } else {
      return null;
    }
  }
}

void main() {
  print(A().getText()); // hello
  print(B().getText()); // null
}

该类B覆盖了textfinal 字段,因此它在第一次被询问时返回一个值,但null在此之后返回。您不能A以可以防止这种形式的覆盖被允许的方式编写您的类。

所以我们不能改变getTextfrom String?to的返回值,String即使看起来我们在返回它之前检查了该text字段。null

于 2020-12-26T14:11:55.917 回答
1

值可以为 'null' 的表达式必须先进行 null 检查,然后才能取消引用。在取消引用之前尝试检查该值是否为“null”。

似乎这确实只适用于局部变量。此代码没有错误:

class Foo {
  String? _a;
  void foo() {
    final a = _a;
    if (a != null) {
      a += 'a';
      _a = a;
    }
  }
}

不过有点烂。我的代码现在充满了仅将类成员复制到局部变量并再次返回的代码。:-/

必须初始化不可为空的实例字段“_a”。尝试添加初始化表达式,或在此构造函数中添加字段初始化程序,或将其标记为“迟到”。

啊,原来“字段初始化器”实际上是这样的:

class Bar {
  Bar() : _a = 'a';
  String _a;
}
于 2020-12-26T13:55:51.467 回答
0

处理这种情况的方法很少。我在这里给出了详细的答案,所以我只是从中写出解决方案:

  • 使用局部变量(推荐)

    void foo() {
      var a = this.a; // <-- Local variable
      if (a != null) {
        a += 'a';
        this.a = a;
      }
    }
    
  • 利用 ??

    void foo() {
      var a = (this.a ?? '') + 'a';
      this.a = a;
    }
    
  • 使用 Bang 运算符 (!)

    只有当您 100% 确定变量 ( a) 不在null您使用它的时候,您才应该使用此解决方案。

    void foo() {
      a = a! + 'a'; // <-- Bang operator
    }
    

回答你的第二个问题:

应始终初始化不可为空的字段。一般有三种初始化方式:

  • 在声明中:

    class Bar {
      String a = 'a';
    }
    
  • 在初始化形式

    class Bar {
      String a;
    
      Bar({required this.a});
    }
    
  • 在初始化列表中:

    class Bar {
      String a;
    
      Bar(String b) : a = b;
    }
    
于 2021-05-15T15:29:30.200 回答
-1

你可以像这样在空安全中创建你的类

    class JobDoc {
  File? docCam1;
  File? docCam2;
  File? docBarcode;
  File? docSignature;

  JobDoc({this.docCam1, this.docCam2, this.docBarcode, this.docSignature});

  JobDoc.fromJson(Map<String, dynamic> json) {
    docCam1 = json['docCam1'] ?? null;
    docCam2 = json['docCam2'] ?? null;
    docBarcode = json['docBarcode'] ?? null;
    docSignature = json['docSignature'] ?? null;
  }
}
于 2021-06-30T12:24:37.920 回答