dart 如何优雅的避空

2019-02-23 17:32:21 +08:00
 AndroidTraveler

前言

对于每一个程序员来说,空指针异常应该是基本都会遇到过的异常,而且这个异常出现的概率还比较大。

但是,空指针异常又是最容易解决的异常,因为只要加个非空判断就可以避免了。

本篇通过对比一般非空判断和 dart 特有的语法糖告诉你如何使用 dart 进行优雅的避空。

目录

1. dart 在线编辑器

一般一些简单的 dart 测试我们可以直接用在线编辑器来做测试和验证。

下面给大家介绍的两个都是官网的。

dart 在线运行器主页版: 👇
https://www.dartlang.org/guides/get-started

dart 在线运行器全屏版: 👇
https://dartpad.dartlang.org/null

其中全屏版就是在主页版里面点击全屏按钮就打开了。

所以可以认为是一样的。

但是笔者使用起来的不同如下,大家可以根据自己的感受选择。

主页版:
优点:运行输出结果较全屏版快。
缺点:输出结果区域较小,超出需要滑动查看。

全屏版:
优点:输出结果区域大。可以直观看到结果。 缺点:运行输出结果较主页版慢。

2. dart ?.

dart 语法糖 ?.

它的意思是左边如果为空返回 null,否则返回右边的值。

A?.B
如果 A 等于 null,那么 A?.B 为 null
如果 A 不等于 null,那么 A?.B 等价于 A.B

Sample:

void main() {
  Animal animal = new Animal('cat');
  Animal empty = null;
  
  //animal 非空,返回 animal.name 的值 cat
  print(animal?.name);
  //empty 为空,返回 null
  print(empty?.name);
  
  //animal 非空,可以直接访问 animal.name 的值 cat
  print(animal.name);
  //empty 为空,抛出异常
  print(empty.name);
}

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

大家拷贝代码然后替换在线编辑器的内容,运行后会看到如下输出:

cat
null
cat
Uncaught exception:
Cannot read property 'get$name' of null

可以看到假设左边不为空,不管是使用**?.还是直接用我们熟悉的.访问变量都是没问题的。
但是如果左边为空,使用
?.会返回null**。但是直接使用**.**会直接抛出异常。

3. dart ??

dart 语法糖 ??

它的意思是左边如果为空返回右边的值,否则不处理。

A??B
如果 A 等于 null,那么 A??B 为 B
如果 A 不等于 null,那么 A??B 为 A

以上面为例子,假设我们上面要求当 empty 为空时,默认值输出 unknown。

那么可以修改如下:

//empty 为空,返回 null
print(empty?.name);

改为

//empty 为空,本来要返回 null,由于有 ??,返回 unknown
print(empty?.name??'unknown');

这样就不会返回 null 而是返回 unknown。

同样的大家可以试下返回 cat 的语句如果加上这个会怎样,可以预见是不会改变的。

4. dart ?. ?? 优雅所在

这边举例说明下使用 ?. ?? 语法糖和不使用的对比。

void main() {
  C c = new C('Case 1');
  B b = new B(c);
  A a = new A(b);
  
//   C c = new C(null);
//   B b = new B(c);
//   A a = new A(b);
  
//   C c = new C('Case 2');
//   B b = null;
//   A a = new A(b);
  
  //直接使用.来最终获取 c 的变量 value
  if (a != null && a.bMember != null && a.bMember.cMember != null) {
    print(a.bMember.cMember.value);
  } else {
    print(null);
  }
  
  //直接使用.来最终获取 c 的变量 value,为空时返回 unknown
  if (a != null && a.bMember != null && a.bMember.cMember != null) {
    String value = a.bMember.cMember.value;
    if (value == null) {
      value = 'unknown';
    }
    print(value);
  } else {
    print('unknown');
  }
  
  //dart 使用?.来最终获取 c 的变量 value
  print(a?.bMember?.cMember?.value);
  //dart 使用?.来最终获取 c 的变量 value,为空时使用 ?? 返回 unknown
  print(a?.bMember?.cMember?.value??'unknown');
}

class A {
  final B bMember;
  A(this.bMember);
}

class B {
  final C cMember;
  B(this.cMember);
}

class C {
  final String value;
  C(this.value);
}

这里面有三个 case,另外两个 case 暂时注释掉。

这三个 case 的结果分别为:

Case 1
Case 1
Case 1
Case 1
null
unknown
null
unknown
null
unknown
null
unknown

可以看到 dart 的语法糖很优雅,一行全搞定。

5. print 方法遇到 null

下面这个例子:

void main() {
    String a = null;
    print('exception='+a);
}

你觉得结果是 exception=null 吗?

结果是

Uncaught exception:
Invalid argument: null

原因是因为 print 里面连接的必须是字符串。

因为这里 a 确实是字符串,所以编辑器没有报错。

假设这里 a 为一个对象 A 的变量,会报如下提示:

The argument type 'A' can't be assigned to the parameter type 'String'.

那我们怎么处理?

有两种方法。

方法一:

void main() {
    String a = null;
    print('exception='+'$a');
}

方法二:

void main() {
    String a = null??'null';
    print('exception='+a);
}

注意下面的写法是不行的,原因是 ?? 优先级没有 + 高。需要加小括号。

void main() {
    String a = null;
    print('exception='+a??'null');
}

6. 牛刀小试

知识学以致用才能够巩固。

因此这边出了小题目给大家测试是否完全掌握本篇内容。

答案组成了支付宝口令红包哦~

微信公众号回复「牛刀小试」获取题目。

或者直接点击菜单栏目录->牛刀小试获取。

温馨提示:
如果你输入 3 次还是提示错误(错误过多口令红包会暂时不可用哦),有两种情况。

第一种就是答案错了。

第二种就是领取完了。

答案会在红包领取完之后或一天之后将题目替换为题目+答案。

因为是异步的,所以不一定实时更新哦~

更多阅读:
Flutter 即学即用系列博客—— 01 环境搭建
Flutter 即学即用系列博客—— 02 一个纯 Flutter Demo 说明
Flutter 即学即用系列博客—— 03 在旧有项目引入 Flutter
Flutter 即学即用系列博客—— 04 Flutter UI 初窥
Flutter 即学即用系列博客—— 05 StatelessWidget vs StatefulWidget

2227 次点击
所在节点    程序员
6 条回复
lxmfly123
2019-02-24 11:33:14 +08:00
发错地方了吧!
AndroidTraveler
2019-02-24 21:05:51 +08:00
@lxmfly123 没有吧。没有 Flutter 模块。又不算前端。跟程序员有关,就发到这里了
lxmfly123
2019-02-25 09:34:37 +08:00
这篇文章的内容应该发在公众号里 ,而不是这种讨论板块。

截了一张 程序员 板块目前最新主题的截图,https://imgchr.com/i/k5lgF1,感受一下。
lxmfly123
2019-02-25 09:36:13 +08:00
链接和文字粘一起了,凑合着感受吧。
AndroidTraveler
2019-02-25 10:28:17 +08:00
@lxmfly123 个人公众号也有发。发到这里是为了让更多人看到吧。看到一个 Blog 版块,后面在那边发发。可能最终系列博客总结会在这里发下。
lxmfly123
2019-02-25 16:19:43 +08:00
发现你的“吧”字比“的”字用得还多~~~

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/537984

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX