你真的搞明白了 Dart 中两个对象相等的逻辑了吗?

前言

通常来说我们不会去实现类的自定义的相等判断,这个时候构建的任何对象因为值不同或 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判断的。intdouble 都继承自数值类num。对于这个类型来说,hash code 就是数值本身,而==操作符实际使用的是 compareTo 方法进行判断的,因此只要两个数值类的值相等(特殊值除外,比如 double.nandouble.infinity),那么使用==比较符操作时就是相等的。

对于字符串类型来说也是一样,字符串的只要字符序列是一致的,那么 hash code就也是一致的。但是,对于 unicode 而言,如果使用的编码不同,那么 hash code 是不相等的。因此,在 Dart 中,比较字符串相等不需要使用类似 Java 的 equals 方法,直接使用==操作符就可以了。

这其实也就给了我们另一种灵活性,比如我们想要两个同一类型的对象相等时,可以覆写==操作符和 hashCode 方法,来实现我们某些用途。例如 Widget 是否要刷新,再比如我们在 Redux 中讲到的,由于每次 Redux 都会返回一个新的 State 对象,如果在实际数据没变的情况下要减少刷新,那么也可以这么操作。

有了上面的认识,我们来看在自定义对象相等判断时的注意事项。

如果覆盖==操作符的话,务必同时覆盖 hashCode 方法

默认的 hashCode 方法会产生一个唯一的 hash 值——这意味着正常情况下,只有两个对象是统一对象时,他们的两个哈希值才会相等。当我们要覆盖==操作符时,意味着我们对这个类的对象相等的判断有其他的定义。对象相等的原则必须满足二者同时具有相同的哈希值。因此,如果你不覆盖 hashCode 方法,意味着在某些场合会失效,比如 Map 以及其他基于哈希值判断的集合,即便两个集合里面的元素满足相等条件,但因为相等元素的哈希值不同,导致两个集合无法满足相等判断。

==操作符应该满足数学意义上的相等规则

数学上,相等需要满足三个规则:

  • 反身性:即a == a 应该始终返回 true
  • 对称性:若a == b 那么 b == a 也应该为 true
  • 传递性:若a == bb == c,那么a == c 也应该为 true

这意味着我们的 hashCode 方法或==操作符方法不能有基于条件来过滤对象的某些属性相等判断。比如跳过对象属性为 null 的情况,就可能导致啥给你们的三个规则中的某一条失效。

对于可变类,应该避免自定义相等判断

什么是可变类?就是对象的属性在运行过程中可能改变的类。因为,考虑上面的数学意义的相等规则,那么自定义相等在生成哈希值时,应当将对象的所有属性都考虑在内。而如果这些属性在运行过程中会被改变的话,那就意味着这个对象的哈希值是会变的。这其实违反了反身性原则(同一个对象,前后的哈希值不相等,这就好比你的女友换了个发型后你就认不出来一样,是要被打的),通时对于基于哈希值的集合来说,没法预料这种变化,这会导致集合的相等判断出错。

不要将==操作符的参数应用于可为空的对象

在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 中对象相等的机制,以及自定义类对象相等判断的注意事项。大部分情况下,我们不会需要自己覆盖对象相等判断,但是在某些场合需要用到的时候,请遵循这些建议,以避免出现莫名其妙的问题。

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

推荐阅读更多精彩内容