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

本文收录于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。

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349

推荐阅读更多精彩内容

  • 本文收录于dart入门潜修系列教程。 创作不易,转载还请备注。 前言 本篇文章会对dart语言的基本语法和内置类型...
    寒潇2018阅读 1,084评论 1 7
  • 本文收录于 kotlin入门潜修专题系列,欢迎学习交流。 创作不易,如有转载,还请备注。 基础语法 基本类型 学习...
    寒潇2018阅读 877评论 0 1
  • 一、Python简介和环境搭建以及pip的安装 4课时实验课主要内容 【Python简介】: Python 是一个...
    _小老虎_阅读 5,725评论 0 10
  • 运算符是处理数据的基本方法,用来从现有的值得到新的值。JavaScript 提供了多种运算符,本章逐一介绍这些运算...
    徵羽kid阅读 667评论 0 0
  • 运算符是处理数据的基本方法,用来从现有的值得到新的值。JavaScript 提供了多种运算符,本章逐一介绍这些运算...
    许先生__阅读 598评论 0 3