前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Dart 点将台 | operator 运算符重载

Dart 点将台 | operator 运算符重载

作者头像
张风捷特烈
发布2021-03-02 11:36:40
4670
发布2021-03-02 11:36:40
举报

@charset "UTF-8";.markdown-body{word-break:break-word;line-height:1.75;font-weight:400;font-size:15px;overflow-x:hidden;color:#333}.markdown-body h1,.markdown-body h2,.markdown-body h3,.markdown-body h4,.markdown-body h5,.markdown-body h6{line-height:1.5;margin-top:35px;margin-bottom:10px;padding-bottom:5px}.markdown-body h1:first-child,.markdown-body h2:first-child,.markdown-body h3:first-child,.markdown-body h4:first-child,.markdown-body h5:first-child,.markdown-body h6:first-child{margin-top:-1.5rem;margin-bottom:1rem}.markdown-body h1:before,.markdown-body h2:before,.markdown-body h3:before,.markdown-body h4:before,.markdown-body h5:before,.markdown-body h6:before{content:"#";display:inline-block;color:#3eaf7c;padding-right:.23em}.markdown-body h1{position:relative;font-size:2.5rem;margin-bottom:5px}.markdown-body h1:before{font-size:2.5rem}.markdown-body h2{padding-bottom:.5rem;font-size:2.2rem;border-bottom:1px solid #ececec}.markdown-body h3{font-size:1.5rem;padding-bottom:0}.markdown-body h4{font-size:1.25rem}.markdown-body h5{font-size:1rem}.markdown-body h6{margin-top:5px}.markdown-body p{line-height:inherit;margin-top:22px;margin-bottom:22px}.markdown-body strong{color:#3eaf7c}.markdown-body img{max-width:100%;border-radius:2px;display:block;margin:auto;border:3px solid rgba(62,175,124,.2)}.markdown-body hr{border:none;border-top:1px solid #3eaf7c;margin-top:32px;margin-bottom:32px}.markdown-body code{word-break:break-word;overflow-x:auto;padding:.2rem .5rem;margin:0;color:#3eaf7c;font-weight:700;font-size:.85em;background-color:rgba(27,31,35,.05);border-radius:3px}.markdown-body code,.markdown-body pre{font-family:Menlo,Monaco,Consolas,Courier New,monospace}.markdown-body pre{overflow:auto;position:relative;line-height:1.75;border-radius:6px;border:2px solid #3eaf7c}.markdown-body pre>code{font-size:12px;padding:15px 12px;margin:0;word-break:normal;display:block;overflow-x:auto;color:#333;background:#f8f8f8}.markdown-body a{font-weight:500;text-decoration:none;color:#3eaf7c}.markdown-body a:active,.markdown-body a:hover{border-bottom:1.5px solid #3eaf7c}.markdown-body a:before{content:"⇲"}.markdown-body table{display:inline-block!important;font-size:12px;width:auto;max-width:100%;overflow:auto;border:1px solid #3eaf7c}.markdown-body thead{background:#3eaf7c;color:#fff;text-align:left}.markdown-body tr:nth-child(2n){background-color:rgba(62,175,124,.2)}.markdown-body td,.markdown-body th{padding:12px 7px;line-height:24px}.markdown-body td{min-width:120px}.markdown-body blockquote{color:#666;padding:1px 23px;margin:22px 0;border-left:.5rem solid;border-color:#42b983;background-color:#f8f8f8}.markdown-body blockquote:after{display:block;content:""}.markdown-body blockquote>p{margin:10px 0}.markdown-body details{outline:none;border:none;border-left:4px solid #3eaf7c;padding-left:10px;margin-left:4px}.markdown-body details summary{cursor:pointer;border:none;outline:none;background:#fff;margin:0 -17px}.markdown-body details summary::-webkit-details-marker{color:#3eaf7c}.markdown-body ol,.markdown-body ul{padding-left:28px}.markdown-body ol li,.markdown-body ul li{margin-bottom:0;list-style:inherit}.markdown-body ol li .task-list-item,.markdown-body ul li .task-list-item{list-style:none}.markdown-body ol li .task-list-item ol,.markdown-body ol li .task-list-item ul,.markdown-body ul li .task-list-item ol,.markdown-body ul li .task-list-item ul{margin-top:0}.markdown-body ol ol,.markdown-body ol ul,.markdown-body ul ol,.markdown-body ul ul{margin-top:3px}.markdown-body ol li{padding-left:6px}.markdown-body ol li::marker{color:#3eaf7c}.markdown-body ul li{list-style:none}.markdown-body ul li:before{content:"•";margin-right:4px;color:#3eaf7c}@media (max-width:720px){.markdown-body h1{font-size:24px}.markdown-body h2{font-size:20px}.markdown-body h3{font-size:18px}}

1. operator 关键字的使用

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

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) 看起来自然很多。

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.运算符重载注意点

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

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 ,就可以得到身高最高的人。

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 值。

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

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

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

无参有返回值

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

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 的字。虽然方便一丢丢,但语义上不怎么好,这里只是演示而已。

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

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

两参无返回值

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

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,可以在其中处理复杂逻辑和异常抛出。但不要忘记加注释,否则别人看不懂。

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 对象可以用 [][]= 运算符,都是内部进行运算符重载的功劳。

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

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

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

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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. operator 关键字的使用
  • 2.运算符重载注意点
  • 3. 运算符重载的类别
    • 单参布尔返回值
      • 无参有返回值
        • 一参有返回值
          • 两参无返回值
          • 4.源码中的运算符重载
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档