V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
AndroidTraveler
V2EX  ›  程序员

dart 如何优雅的避空

  •  
  •   AndroidTraveler · 2019-02-23 17:32:21 +08:00 · 2376 次点击
    这是一个创建于 2102 天前的主题,其中的信息可能已经有所发展或是发生改变。

    前言

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

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

    本篇通过对比一般非空判断和 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

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

    截了一张 程序员 板块目前最新主题的截图,https://imgchr.com/i/k5lgF1,感受一下。
    lxmfly123
        4
    lxmfly123  
       2019-02-25 09:36:13 +08:00
    链接和文字粘一起了,凑合着感受吧。
    AndroidTraveler
        5
    AndroidTraveler  
    OP
       2019-02-25 10:28:17 +08:00
    @lxmfly123 个人公众号也有发。发到这里是为了让更多人看到吧。看到一个 Blog 版块,后面在那边发发。可能最终系列博客总结会在这里发下。
    lxmfly123
        6
    lxmfly123  
       2019-02-25 16:19:43 +08:00
    发现你的“吧”字比“的”字用得还多~~~
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3185 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 21ms · UTC 13:41 · PVG 21:41 · LAX 05:41 · JFK 08:41
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.