美文网首页dart入门潜修
dart入门潜修基础篇之操作符

dart入门潜修基础篇之操作符

作者: 寒潇2018 | 来源:发表于2019-02-01 17:26 被阅读0次

    本文收录于dart入门潜修系列教程

    创作不易,转载还请备注。

    概览

    dart同其他语言一样,提供了丰富的操作符,什么是操作符?其实,我们常见的 +、-、*、/ 等都是操作符,这些操作符很多都对应于数学知识中的概念。使用操作符能大大简化我们的代码,并使之具有较高的可读性。本篇文章将对dart中的操作符进行阐述。

    算术操作符

    dart中,支持的算术操作符如下所示:

    操作符 含义
    + 加法操作
    - 减法操作
    * 乘法操作
    / 除法操作,返回浮点数
    ~/ 除法操作,但返回整数
    % 求余数操作

    结合上面的运算符及其含义,我们来看下示例:

    void main() {
      print(1 + 2); //打印 3
      print(1 - 2); //打印 -1
      print(1 * 2); //打印 2
      print(1 / 2); //打印 0.5
      print(1 % 2); //打印 1
      print(1 ~/ 2); //打印 0
    }
    

    需要注意的是,除法操作符 / 和其他很多语言的意义不一样,这里不再简单取整,而是返回实际的浮点数。取整的功能则有另一个操作符 ~/ 来完成,操作符 ~/ 相当于取整操作,会舍弃小数。

    自增自减操作符

    dart中的自增操作符同样分为两种,一种是前缀自增,一种是后缀自增,如下所示:

    操作符 含义
    ++a 前缀自增操作符,相当于a = a + 1,表达式的返回值是a+1
    a++ 后缀自增操作符,相当于a = a + 1,表达式的返回值是a
    --a 前缀自减操作符,相当于a = a-1,表达式的返回值为a - 1
    a-- 后缀自减操作符,相当于a = a -1,表达式的返回值是a

    来看个自增自减操作符的示例:

    void main() {
      var a = 0;
      var b = 0;
      b = a++;
      print("a = $a; b = $b"); //打印 a = 1; b = 0
    
      a = 0;
      b = ++a;
      print("a = $a; b = $b"); //打印 a = 1; b = 1
    
      a = 0;
      b = a--;
      print("a = $a; b = $b"); //打印 a = -1; b = 0
    
      a = 0;
      b = --a;
      print("a = $a; b = $b"); //打印 a = -1; b = -1
    }
    

    由此可见,前缀表达式和后缀表达式最终的区别就是:前缀表达式除了自身改变外,其表达式的返回值也随之改变;而后缀表达式只有自身的值会改变,但其表达式的返回值依然是原来的旧值。

    比较操作符

    dart提供的比较操作符罗列如下:

    操作符 含义
    == 等于操作符
    != 不等于操作符
    > 大于操作符
    < 小于操作符
    >= 大于等于操作符
    <= 小于等于操作符

    这些比较操作符主要需要关注的是 == 操作符,我们知道在很多编程语言中,== 操作符并不像表面那样,看着内容相等就相等,这里还涉及到对象的比较以及对象内容的比较。先来看个例子:

    //简单定义一个Person类,这里涉及到了面向对象的
    //知识,会在后续的文章中进行阐述。
    class Person {
      String id;
      Person(String id) {
        this.id = id;
      }
    }
    //测试方法main
    void main() {
      Person p = Person("1234");
      Person p1 = p;
      print(p == p1);//打印 true
    
      Person p2 = Person("1234");
      print(p == p2);//打印 false
    }
    

    在上面代码中,我们定义了一个Person类,然后生成了id=1234的实例p和p2,最后通过比较,我们发现p竟然不等于p2,按照常理,id相同的人显然他们应该是同一个人!但是== 操作符却不这么认为,默认的 == 操作符比较的实际上是两个对象的地址,如果两个对象地址一致则相等,否则不相等,所以p==p1为true,而p==p2为false。

    那么如果我们觉得两个id相等的人就应该是同一个人,该如何进行比较呢?这就涉及到了操作符重载。

    操作符重载是指,我们改变操作符默认行为的操作,上面除了 !=操作符不能被重载(!=操作符,实际上对应的是 !(obj1 == obj2) ),其他的都可以进行重载,比如我们现在想要得到id相等的人就是同一个人的期望,那么我们就可以重载==操作符,如下所示:

    class Person {
      String id;
      Person(String id) {
        this.id = id;
      }
    //重点在这里,我们重载了操作符==
      bool operator ==(Person p) => this.id == p.id;
    }
    //测试
    void main() {
      Person p = Person("1234");//打印 true
      Person p1 = p;
      print(p == p1);
    
      Person p2 = Person("1234");//打印 true
      print(p == p2);
    }
    

    这样两个id相等的人就是同一个人了!

    然而这也带来一个问题,如果我们操作失误,确实把两个不同的人写成了同一个id,但是他们确实是不同的人,而Person类本身又已经复写了==操作符,此时我们该怎么办?

    这个就需要dart为我们提供的另一个方法了:identical,identical方法会精确比较两个对象的地址,不相等就是不相等,如下所示:

    class Person {
      String id;
    
      Person(String id) {
        this.id = id;
      }
    //我们同样复写了==操作符
      bool operator ==(Person p) => this.id == p.id;
    }
    //测试
    void main() {
      Person p = Person("1234");
      Person p1 = p;
      Person p2 = Person("1234");
    
      print(identical(p, p1));//打印 true
      print(identical(p, p2));//注意这里,打印为 false
    }
    

    由此可知,identical方法不会关心你有没有重载==操作符,它会进行精确比较。

    当然对于整型的比较我们无需关注这些,只要它们的内容相等即为相等,但是对于浮点数的比较,我还是有责任再引入一个众多语言都存在的一个经典的比较问题,来看个例子:

    void main() {
      var k = 0.1 + 0.2;
      print(k);//打印 0.30000000000000004
      print(k == 0.3);//打印 false !
    }
    

    是的,这就是浮点数比较的陷阱,dart同样无法避免,这是浮点数在计算机内部存储机制造成的,可自行查找相关资料。这里只是给出比较建议,类似于这样的场景,完全可以根据自己的需要进行取舍,我们可以结合两个浮点数的差值大小来进行判断,比如当两个浮点数之差小于10-6的时候就认为这两个数相等就可以了。

    类型相关操作符

    类型相关操作符,主要用于在运行时检查实例的类型是否匹配或者进行类型转换等,dart中提供了三个类型相关的操作符,如下所示:

    操作符 含义
    as 类型转换
    is 如果对象类型是匹配的类型则返回true
    is! 如果对象类型匹配的类型则返回false

    这三个操作符都比较简单,以上述Person类为例,来看下他们的效果:

    //定义了一个Person类
    class Person {
      String id;
      String name;
      Person(String id) {
        this.id = id;
      }
    //复写toString的方法,目的是打印数据
      String toString() {
        return this.name;
      }
    }
    //测试
    void main() {
      Object p = Person("1234");//注意这里的p是Object类型,Object是dart中所有类的基类
    //检测p是否是Person类型,这里p的实际类型确实是Person,所以为true
      if (p is Person) {
        p.name = "张三";
      }
      print(p);//打印"张三"
    //注意这里的str同样是Object类型,但是其实际类型是字符串
      Object str = "test";
      if (str is Person) {//str不是Person类型,此处为false
        str.name = "李四";
      }
      print(str);//打印 test
    }
    

    上面代码展示了is的用法,is! 操作符刚好与其相反,这里不再演示。有一点需要注意,我们无法使用精确的类型去检测,比如上面我们将str定义为String str = "test",这个时候代码就会编译不通过,因为编译器已经知道了str的类型为String,所以在编译str.name这句语句的时候,就会报没有name这个字段的异常。

    上面的代码显得有点麻烦,其实我们可以结合as操作符简化上述代码,如下所示:

    void main() {
      Object p = Person("1234");
      (p as Person).name = "张三";//这里使用as操作符对p进行了转换
      print(p);//打印 张三
    }
    

    上面代码演示了as操作符的用法,但是需要注意,如果p为null或者不符合匹配的类型的时候,as操作符将会抛出异常。

    赋值操作符

    dart中最直接的操作符就是单等于号(=),这个其实在上面例子中已经使用了很多次,= 操作符就是为变量赋值,而不用关心该变量是不是为null。实际上dart还为我们提供了 ??= 操作符,该操作符只有在变量为null的时候才会对变量进行赋值,如下所示:

    void main() {
      String str = "first str";
      str ??= "second str";
      print(str);//打印 first str
      str = null;
      str ??= "third str";//打印 third str
      print(str);
    }
    

    除此之外,dart还支持很多扩展的赋值操作符,这里罗列如下:

    操作符 含义
    -= a -= b,相当于a = a -b
    /= a /=b,相当于a = a / b
    %= a %=b,相当于a = a % b
    >>= a >>= b,相当于a = a >> b
    ^= a ^=b,相当于a = a ^ b
    += a +=b,相当于a = a + b
    *= a *= b,相当于a = a * b
    ~/= a ~/= b,相当于a = a ~/ b
    <<= a <<= b,相当于a = a << b
    &= a &= b,相当于a = a & b
    |= a |=b,相当于a = a | b

    这些操作符的含义还是比较容易理解的,这里不再展开阐述。

    逻辑操作符

    逻辑操作符主要用于布尔表达式判断,dart中的逻辑操作符主要三种,如下所示:

    操作符 含义
    !expression 非运算符,expression为false时返回true,否则返回false
    || 或运算操作符,其连接的表达式都为false时返回false,否则返回true
    && 并运算操作符,其连接的表达式都为true时返回true,否则返回false

    在dart中,同样对逻辑操作符进行了优化,即会执行“截断操作”,如下所示:

    //这里有个判断是否为空的方法,方法体在返回之前
    //打印了一条语句,用于判断该方法是否执行
    bool isEmpty() {
      print("is empty...");
      return false;
    }
    //测试
    void main() {
      bool isNull = false;
    
      if (isNull && isEmpty()) {//这个if代码块执行完成后,没有任何信息
        print("is not blank");
      }
    
      isNull = true;
      if (isNull && isEmpty()) {//这个执行完成后打印 is empty...
        print("is blank");
      }
    }
    

    上面代码是以 && 操作符为例,从中可以看出,当第一个表达式(即isNull)为false的时候,后面表达式(即isEmpty())就不会再执行。其他操作符同样也有对应的优化,这里不再阐述。

    位操作符

    dart支持多种位运算,罗列如下:

    操作符 含义
    & 位与操作符
    | 位或操作符
    ^ 位异或操作符
    ~expression 取反操作符
    << 左移操作符
    >> 右移操作符

    单纯的演示位操作符意义不大,因为无法直观的看出表达式和其结果的对应关系,还需要手动的计算才可以验证,不过计算机的输出结果是不用怀疑的,所以主要简单关注下其语法即可,如下所示:

    void main() {
      var val = 0x01;
      var mask = 0xff;
      print(val & mask);
      print(val & ~mask);
      print(val | mask);
      print(val ^ mask);
      print(val << 4);
      print(val >> 4);
    }
    

    条件表达式

    提起“条件”两个字,很自然想到的就是if else语句,确实if else能够解决各种条件判断问题,但是dart提供了两种更简洁的条件表达式,其语法如下所示:

    //第一种
    //当条件condition为true时执行expr1并返回其值
    //当条件condition为false时执行expr2并返回其值
    condition ? expr1 : expr2
    
    //第二种
    //当expr1不为null时返回其自身值,否则执行expr2并返回其值
    expr1 ?? expr2
    

    来看个例子:

    void main() {
      bool isOk = false;
      print(isOk ? "is ok " : "is not ok");//打印 is not ok
    
      String str;
      print(str ?? "test");//打印test,因为此时str为null,所以返回test
      str = "test";
      print(str ?? "test2");//打印test,因为此时str不为null,所以返回其自身
    }
    

    展开符

    首先明确一点,在dart官方文档中,将展开符定义为了dart的一种语法,而不是定义为一个操作符,其修饰关键字是两个点(..) 展开操作符示例如下:

    class Person {
      String name;
      int age;
    //复写toString方法,方便打印
      String toString() {
        return "name : $name ; age :$age";
      }
    }
    //测试
    void main() {
      Person p = Person()//使用展开符完成赋值
        ..name = "张三"
        ..age = 20;
     
      print(p);//打印 name : 张三 ; age :20
    }
    

    其他操作符

    除了上面的操作符,dart还提供了一些其他的操作符,如下所示:

    操作符 含义
    ( ) 方法调用操作符
    [ ] 数组访问操作符
    . 成员访问操作符
    ?. 有条件的成员访问操作符

    这主要来看一下 ?. 操作符,这个操作符也是成员访问操作符,只不过它是有条件的访问,来看个例子:

    void main() {
      Person p = Person()
        ..name = "张三";
      print(p.name);//打印 张三
      print(p?.name);//打印 张三
    
      p = null;//此时我们将p设置为null
      print(p?.name);//打印null
      print(p.name);//!!!运行时错误,会crash
    }
    

    由代码可知,操作符 . 和 ?. 唯一的区别就是在当对象为null的时候,操作符 . 会直接crash,而 ?. 操作符则直接返回null。

    至此,本篇文章阐述完毕。

    相关文章

      网友评论

        本文标题:dart入门潜修基础篇之操作符

        本文链接:https://www.haomeiwen.com/subject/jtvtkqtx.html