Dart 点将台 | operator 运算符重载


主题列表:juejin, github, smartblue, cyanosis, channing-cyan, fancy, hydrogen, condensed-night-purple, greenwillow, v-green, vue-pro, healer-readable, mk-cute, jzman, geek-black, awesome-green, qklhk-chocolate

贡献主题:https://github.com/xitu/juejin-markdown-themes

theme: v-green

highlight:

1. operator 关键字的使用

Dart 中支持对运算符的重载,这里先通过一个小例子看看如何使用 operator 关键字。我们说 捷特比龙少大 , 一般指的是年龄。如下的 Person 对象中重写 > ,来比较当前 Person 和另一个人的大小。这样的好处是在语义上更清晰,读码时也比较直观。

```dart main() { Person toly = Person('捷特', 26); Person ls = Person('龙少', 25); print(toly > ls); // true }

class Person { String name; int age;

Person(this.name, this.age); bool operator >(Person other) => this.age > other.age; } ```


如下 Person 类中定义一个 lg 的方法,也可以实现两人年龄大小的比较。但 toly > ls 要比 toly.lg(ls) 看起来自然很多。

```dart main() { Person toly = Person('捷特', 26); Person ls = Person('龙少', 25); print(toly > ls); // true print(toly.lg(ls)); // true }

class Person { String name; int age;

Person(this.name, this.age); // > 运算符重载 bool operator >(Person other) => this.age > other.age; // 使用方法比较大小 bool lg(Person other) => this.age > other.age; } ```

不过尺有所短寸有所长,使用 > 重载的灵活性比使用方法差,运算符只能重新一个,但方法可以随意定义。所以对应一个对象而言,想要一个和运算符相关的行为看起来更自然,可以考虑使用运算符重载。


2.运算符重载注意点

运算符重载只是相当于简化书写方便阅读,其本身并没有硬性的规定。比如下面使用 | 运算符,来返回较高的人。肯能别人看你的代码时就会不知所云,当比较特别的运算符重载时,最好进行注释。

```dart main() { Person toly = Person('捷特', 26, 180); Person ls = Person('龙少', 25, 179);

Person taller = ls | toly; print(taller.name); // 捷特 }

class Person { String name; int age; int height;

Person(this.name, this.age, this.height); bool operator >(Person other) => this.age > other.age; // 返回较高的人 Person operator |(Person other) => this.height > other.height ? this : other; }

```


这样在无意间也能产生很有趣的效果,比如 wy | ls | toly ,就可以得到身高最高的人。

```dart main() { Person toly = Person('捷特', 26, 180); Person ls = Person('龙少', 25, 179); Person wy = Person('巫缨', 25, 179); print(toly > ls); // true

Person taller = wy | ls | toly; print(taller.name); // 捷特 } ```


3. 运算符重载的类别

Dart 中并非所有的运算符都可以重载,也并非所有的运算符重载的格式都一样。不同的运算符重载,返回值、入参会有所差异。下面是我按照 返回值、入参 进行的分类,图中同色的运算符重载在语法上使用是一致的,只是语义上有所区别。也就是说,它们的用法本质上是一样的,但原则上要根据语义使用。


单参布尔返回值

第一排的 >、<、>=、<=、== 需要传入一个对象,返回bool 值。

dart bool operator ==(Object other) { return other is Person && other.name == name && other.height == height && other.age == age; }

但仍要知道 == 符号也只是个标识而已,你换成 >= ,在使用时通过 >= 判断两个 Person 是否相等,在逻辑上是没有问题的。但在现实中可能被人打,运算符重载应该首先尊重语义,语义不明的运算符重载,不如不用。

dart bool operator >=(Object other) { return other is Person && other.name == name && other.height == height && other.age == age; }


无参有返回值

只要一个 ~ 运算符,不需要提供参数,且可以返回对象,这个对象类型是任意的,想返回什么类型都可以。比如下面通过 ~ 重载,返回另一个属性一样的 Person 对象,实现对象的拷贝。当已经有了 toly 这个对象,只要使用 ~toly 实现对象拷贝。

```dart main() { Person toly = Person('捷特', 26, 180); print(~toly); // Person{name: 捷特, age: 26, height: 180} print(identical(~toly,toly)); // false }

// ~ 运算符重载 Person operator ~() => Person( this.name, this.age, this.height ); ```


一参有返回值

黄色的几个运算符重载,都需要一个入参,返回一个对象。注意,参数和返回值的类型任意。如下通过 [] 获取名字的第 i 个字。如果越界,则取最后一个字。通过 toly[3] 即可获得名字的索引位 3 的字。虽然方便一丢丢,但语义上不怎么好,这里只是演示而已。

```dart Person toly = Person('张风捷特烈', 26, 180); print(toly[3]); // 特

// 运算符重载 String operator { if (index >= name.length) return name[name.length - 1]; return this.name[index]; } ```


两参无返回值

无返回值可重载的操作符只有 []= ,既然没有返回值,那就说明它是用来修改类内部属性的。如下,通过 []= 来修改名字中的第 i 个字符。这样想修改某一个字符就会方便很多,避免重复些处理的逻辑。

```dart Person toly = Person('张风捷特烈', 26, 180); toly[2]= "杰"; print(toly); // Person{name: 张风杰特烈, age: 26, height: 180}

// 运算符重载 void operator []=(int index,String char) { if (index >= name.length){ name = name.substring(0,name.length-1)+char; }else{ String tail = name.substring(index+1); String front = name.substring(0,index); name = front+char+tail; } } ```


为了让你更清楚运算符重载的随意性,这里再写一个小例子。比如现在有个结婚和离婚的逻辑,当然可以用方法来实现逻辑,但如下代码 lx[wy] = true; 后就可以实现。这里只是举个小栗子,说明运算符重载并不是固定和单调的,可以根据不同场景进行 DIY,可以在其中处理复杂逻辑和异常抛出。但不要忘记加注释,否则别人看不懂。

```dart main() { Person lx = Person("林兮", Gender.male); Person wy = Person("巫缨", Gender.female);

lx[wy] = true; // 两人结婚 print(wy); // Person{name: 巫缨, gender: Gender.female, spouse: 林兮}

wy[lx] = false;// 两人离婚 print(wy); // Person{name: 巫缨, gender: Gender.female, spouse: null} }

enum Gender { male, female }

class Person { String name; Gender gender; Person _spouse;

Person(this.name, this.gender);

// 是否单身 bool get single => _spouse == null;

// 和 other 结婚或离婚。 // lx[wy] = true; 两人结婚 // lx[wy] = false; 已婚的两人离婚 void operator []=(Person other, bool flag) { if (this.gender == other.gender) throw Exception("same gender can't spouse!");

bool spoused = identical(this._spouse, other);

// 如果已经结婚 且 flag 为 false,则离婚
if (spoused && !flag) {
  other._spouse = null;
  this._spouse = null;
  return;
}

// 如果都是单身并且 flag 为 true,则两人结婚
if (this.single && other.single && flag) {
  this._spouse = other;
  other._spouse = this;
  return;
}
throw Exception("hi, $name ,you can't spouse or divorce any more!");

}

@override String toString() { return 'Person{name: $name, gender: $gender, spouse: ${_spouse?.name}}'; } } ```


4.源码中的运算符重载

也许有人觉得 Dart 运算符重载很少见,其实你经常在用,只是不知道而已。比如 List 对象可以用 [][]= 运算符,都是内部进行运算符重载的功劳。

dart List<int> arr = [0,1,2,3]; arr[0]=100; print(arr[0]); //100


Size 对象可以 + Offset 对象生成新的尺寸,也是因为重写的运算符。

dart Size size = Size(100,200); Offset offset = Offset(100,300); Size s3= size + offset;

源码中有非常多的地方都使用了运算符重载,我们在使用这些对象时更方便,语义性也很好。运算符重载是 Dart 一个非常优秀的特点,但也不要乱用,要尊重语义。好了,本篇就到这里,谢谢观看~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值