前言
通常來說我們不會去實現類的自定義的相等判斷,這個時候構建的任何對象因為值不同或 hash code不同,使用==
判斷的時候都是返回 false
的。而在某些場合狭归,如果我們想要直接使用==
來判斷自定義類是否相等的話,則需要同時覆蓋==
操作符和 hashCode
方法文判。在使用自定義的對象相等性實現時过椎,編碼需要注意哪些事情,本篇來為你總結相關內容戏仓。
對象相等判斷
我們先來看 Java 語言中很經典的一道面試題疚宇,即下面的代碼控制臺輸出結果是什么。
Integer a=127;
Integer b=127;
System.out.println(a==b);
Integer c=128;
Integer d=128;
System.out.println(c==d);
結果第一個打印的是 true赏殃,第二個打印的是 false敷待。這是因為 Java 中的-128至127范圍的整數對象使用了緩存,在這范圍之外的都是新構建的對象了仁热。那么 Dart 中榜揖,類似的情況是什么樣的?
int a = 127;
int b = 127;
print(a == b);
int c = 128;
int d = 128;
print(c == d);
結果返回都是 true
股耽,這是因為 Dart 支持操作符重載根盒,判斷兩個對象相等實際是使用的是==
操作符和 hash code
判斷的钳幅。int
和 double
都繼承自數值類num
物蝙。對于這個類型來說,hash code
就是數值本身敢艰,而==
操作符實際使用的是 compareTo
方法進行判斷的诬乞,因此只要兩個數值類的值相等(特殊值除外,比如 double.nan
钠导,double.infinity
)震嫉,那么使用==
比較符操作時就是相等的。
對于字符串類型來說也是一樣牡属,字符串的只要字符序列是一致的票堵,那么 hash code就也是一致的。但是逮栅,對于 unicode 而言悴势,如果使用的編碼不同窗宇,那么 hash code
是不相等的。因此特纤,在 Dart 中军俊,比較字符串相等不需要使用類似 Java 的 equals
方法,直接使用==
操作符就可以了捧存。
這其實也就給了我們另一種靈活性粪躬,比如我們想要兩個同一類型的對象相等時,可以覆寫==
操作符和 hashCode
方法昔穴,來實現我們某些用途镰官。例如 Widget 是否要刷新,再比如我們在 Redux 中講到的吗货,由于每次 Redux 都會返回一個新的 State 對象朋魔,如果在實際數據沒變的情況下要減少刷新,那么也可以這么操作卿操。
有了上面的認識警检,我們來看在自定義對象相等判斷時的注意事項。
如果覆蓋==操作符的話害淤,務必同時覆蓋 hashCode 方法
默認的 hashCode 方法會產生一個唯一的 hash 值——這意味著正常情況下扇雕,只有兩個對象是統(tǒng)一對象時,他們的兩個哈希值才會相等窥摄。當我們要覆蓋==操作符時镶奉,意味著我們對這個類的對象相等的判斷有其他的定義。對象相等的原則必須滿足二者同時具有相同的哈希值崭放。因此哨苛,如果你不覆蓋 hashCode 方法,意味著在某些場合會失效币砂,比如 Map 以及其他基于哈希值判斷的集合建峭,即便兩個集合里面的元素滿足相等條件,但因為相等元素的哈希值不同决摧,導致兩個集合無法滿足相等判斷亿蒸。
==操作符應該滿足數學意義上的相等規(guī)則
數學上,相等需要滿足三個規(guī)則:
- 反身性:即
a == a
應該始終返回true
掌桩。 - 對稱性:若
a == b
那么 b == a 也應該為true
边锁。 - 傳遞性:若
a == b
且b == c
,那么a == c
也應該為true
波岛。
這意味著我們的 hashCode
方法或==操作符方法不能有基于條件來過濾對象的某些屬性相等判斷茅坛。比如跳過對象屬性為 null 的情況,就可能導致啥給你們的三個規(guī)則中的某一條失效则拷。
對于可變類贡蓖,應該避免自定義相等判斷
什么是可變類祟剔?就是對象的屬性在運行過程中可能改變的類。因為摩梧,考慮上面的數學意義的相等規(guī)則物延,那么自定義相等在生成哈希值時,應當將對象的所有屬性都考慮在內仅父。而如果這些屬性在運行過程中會被改變的話叛薯,那就意味著這個對象的哈希值是會變的。這其實違反了反身性原則(同一個對象笙纤,前后的哈希值不相等耗溜,這就好比你的女友換了個發(fā)型后你就認不出來一樣,是要被打的)省容,通時對于基于哈希值的集合來說抖拴,沒法預料這種變化,這會導致集合的相等判斷出錯腥椒。
不要將==操作符的參數應用于可為空的對象
在Dart 中阿宅,null
只會與其自身相等,因此只有當被比較的對象不為空時才應該調用==
操作符進行相等判斷笼蛛。
// 正確示例
class Person {
final String name;
// ···
bool operator ==(Object other) => other is Person && name == other.name;
}
//錯誤示例
class Person {
final String name;
// ···
bool operator ==(Object? other) =>
other != null && other is Person && name == other.name;
}
注意:在 Dart 推出 null safety 版本以前洒放,對象是允許為 null 的。即便是這樣滨砍,Dart也不會使用 null 調用自定義的==方法進行相等判斷(可以理解為 Dart 直接處理為 false 了)往湿。因此,在非 null safety 版本(< 2.12版本)惋戏,我們無需在==方法內處理 null领追。
總結
本篇介紹了 Dart 中對象相等的機制,以及自定義類對象相等判斷的注意事項响逢。大部分情況下绒窑,我們不會需要自己覆蓋對象相等判斷,但是在某些場合需要用到的時候龄句,請遵循這些建議回论,以避免出現莫名其妙的問題散罕。