Dart 點將臺 | operator 運算符重載

1. operator 關鍵字的使用

Dart 中支持對運算符的重載,這裏先經過一個小例子看看如何使用 operator 關鍵字。咱們說 捷特比龍少大 , 通常指的是年齡。以下的 Person 對象中重寫 > ,來比較當前 Person 和另外一我的的大小。這樣的好處是在語義上更清晰,讀碼時也比較直觀。web

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) 看起來天然不少。markdown

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;
}
複製代碼

不過尺有所短寸有所長,使用 > 重載的靈活性比使用方法差,運算符只能從新一個,但方法能夠隨意定義。因此對應一個對象而言,想要一個和運算符相關的行爲看起來更天然,能夠考慮使用運算符重載。ide


2.運算符重載注意點

運算符重載只是至關於簡化書寫方便閱讀,其自己並無硬性的規定。好比下面使用 | 運算符,來返回較高的人。肯能別人看你的代碼時就會不知所云,當比較特別的運算符重載時,最好進行註釋。ui

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 ,就能夠獲得身高最高的人。this

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 中並不是全部的運算符均可以重載,也並不是全部的運算符重載的格式都同樣。不一樣的運算符重載,返回值、入參會有所差別。下面是我按照 返回值、入參 進行的分類,圖中同色的運算符重載在語法上使用是一致的,只是語義上有所區別。也就是說,它們的用法本質上是同樣的,但原則上要根據語義使用。spa


單參布爾返回值

第一排的 >、<、>=、<=、== 須要傳入一個對象,返回bool 值。code

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

但仍要知道 == 符號也只是個標識而已,你換成 >= ,在使用時經過 >= 判斷兩個 Person 是否相等,在邏輯上是沒有問題的。但在現實中可能被人打,運算符重載應該首先尊重語義,語義不明的運算符重載,不如不用。orm

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 一個很是優秀的特色,但也不要亂用,要尊重語義。好了,本篇就到這裏,謝謝觀看~

相關文章
相關標籤/搜索