V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
AndroidTraveler
V2EX  ›  程序员

dart 如何优雅的避空

  •  
  •   AndroidTraveler · Feb 23, 2019 · 3017 views
    This topic created in 2624 days ago, the information mentioned may be changed or developed.

    前言

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

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

    本篇通过对比一般非空判断和 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 replies    2019-02-25 16:19:43 +08:00
    lxmfly123
        1
    lxmfly123  
       Feb 24, 2019
    发错地方了吧!
    AndroidTraveler
        2
    AndroidTraveler  
    OP
       Feb 24, 2019
    @lxmfly123 没有吧。没有 Flutter 模块。又不算前端。跟程序员有关,就发到这里了
    lxmfly123
        3
    lxmfly123  
       Feb 25, 2019
    这篇文章的内容应该发在公众号里 ,而不是这种讨论板块。

    截了一张 程序员 板块目前最新主题的截图,https://imgchr.com/i/k5lgF1,感受一下。
    lxmfly123
        4
    lxmfly123  
       Feb 25, 2019
    链接和文字粘一起了,凑合着感受吧。
    AndroidTraveler
        5
    AndroidTraveler  
    OP
       Feb 25, 2019
    @lxmfly123 个人公众号也有发。发到这里是为了让更多人看到吧。看到一个 Blog 版块,后面在那边发发。可能最终系列博客总结会在这里发下。
    lxmfly123
        6
    lxmfly123  
       Feb 25, 2019
    发现你的“吧”字比“的”字用得还多~~~
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   2339 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 38ms · UTC 05:16 · PVG 13:16 · LAX 22:16 · JFK 01:16
    ♥ Do have faith in what you're doing.