Dart null 安全性不适用于类字段

Tim*_*mmm 11 dart dart-null-safety

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

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

class Bar {
  Bar() {
    _a = 'a';
  }
  String _a;
}
Run Code Online (Sandbox Code Playgroud)

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

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

对于Bar() {

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

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

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

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

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

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

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

jul*_*101 17

问题是即使类字段被标记为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
}
Run Code Online (Sandbox Code Playgroud)

B类重写了text最后一个字段所以它返回一个值第一次有人问,但回报率null在此之后。您不能A以可以阻止这种形式的覆盖被允许的方式编写您的类。

因此,即使看起来我们在返回字段之前检查了该字段,我们也无法更改getTextfrom的返回值。String?Stringtextnull

  • 在我看来,奇怪的是,一个相当深奥的用例,比如在子类中覆盖final,现在在 NNBD 实现中放置了一个巨大的疣。Flutter 布局将充满“!”,每个布局都会在未来产生潜在的错误,因为替代方案太冗长了。 (4认同)
  • @shawnblais 或者创建对成员值的本地引用。 (3认同)
  • 谢谢@jamesdlin,我相信这实际上是唯一安全的方法。但我认为大多数开发人员只会使用 ! 操作符,然后基本上消除了 NNBD 对于该 var 的任何优势,并创建了将来很容易被破坏的脆弱方法。例如; `如果(索引== null)返回;索引=索引!+ 1;` 如果后来有人删除了这个空检查,编译器什么也不说,手榴弹就在那里。在这个例子中,看起来有点傻,但在超过几行的方法上,这是一个重要的问题,那些“!”运算符不会完全跳到读者那里。 (2认同)
  • @阿德南 没错。getter/setter 方法的目的是它们与普通变量没有区别。从某种意义上说,“final”变量是一个只有 getter 的字段,而“var”变量则同时具有 setter 和 getter。这也意味着我们总是可以用新的 getter 覆盖变量,就像我的例子一样,这是这个问题的核心。(但这也是我们不像 Java 那样创建 `getSomethig()` 和 `setSomehining()` 方法的原因,因为如果我们需要的话,我们总是可以向现有字段引入逻辑,而不需要更改 API。 (2认同)
  • @Adnan在Java中,标准做法是很少公开类变量。相反,我们定义“getVariable()”和“setVariable()”来获取和设置私有变量。原因是 Java 没有 getter/setter 方法的概念(如 Dart 或 C#),因此如果我们稍后想要引入字段验证等功能,我们无法在不更改 API 的情况下做到这一点,除非我们从一开始就有使用一种方法来访问变量。 (2认同)