摘要:總之,在編寫(xiě)單個(gè)類(lèi)的方法時(shí)比較簡(jiǎn)單,當(dāng)涉及子類(lèi)繼承時(shí),就要多考慮一下了。另外不要忘記覆蓋方法哦。
相信讀過(guò) 《Effective Java》 的讀者都已經(jīng)知道編寫(xiě) equals 方法的作用與重要性,基本概念不多做解釋?zhuān)@里就總結(jié)一下如何編寫(xiě)正確的 equals 方法。
equals 在 Java 和 Scala 中含義相同,都需要滿(mǎn)足以下五個(gè)條件:
自反性
對(duì)稱(chēng)性
傳遞性
一致性
anyObject.equals(null) == false
現(xiàn)在我們有三個(gè)問(wèn)題:
假如我們只有一個(gè)類(lèi) Person,如何寫(xiě)?
假如 Person 類(lèi)有一個(gè)子類(lèi) Student,相互不能判斷(一定返回 false),如何寫(xiě)?相互可以判斷,如何寫(xiě)?
假如 Person 和 Student 可以相互判斷,但另一子類(lèi) Teacher 只能和同類(lèi)判斷,如何寫(xiě)?
Java《Effective Java》 中最后推薦的寫(xiě)法步驟是:
通過(guò) == 判斷是否是同一個(gè)對(duì)象
用 instanceof 判斷是否是正確的類(lèi)型,注意這里已經(jīng)包含了 null 的情況,所以不用多帶帶另寫(xiě)
將對(duì)象轉(zhuǎn)換成正確的類(lèi)型
對(duì)需要判斷的域分別進(jìn)行對(duì)比
需要注意,基本類(lèi)型用 == 判斷,例外是 float 用 Float.compare,double 用 Double.compare,因?yàn)橛?NaN 等特殊值存在。
上述第二步中還有另一個(gè)變種,是使用 getClass 進(jìn)行類(lèi)型判斷,這樣的話(huà)只有類(lèi)型完全一致才能返回 true,如果只是單一的類(lèi)還好,要是涉及類(lèi)之間的繼承,則違背了 Liskov Substitution Principle,所以最后書(shū)中的結(jié)論是:
There is no way to extend an instantiable class and add a value component while preserving the equals contract.
由于現(xiàn)在的 IDE 例如 IntelliJ IDEA 已經(jīng)可以自動(dòng)為我們生成 equals 方法,還可以選擇是否允許子類(lèi)判斷,是否可為 null 等判斷,所以我們就不必手動(dòng)編寫(xiě)了,但是生成的結(jié)果也是符合上面的 4 步的:
class Person{ private String name; private int age; @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { // 不涉及繼承,問(wèn)題 1 和 問(wèn)題 2 前半的寫(xiě)法 return false; } Person person = (Person) o; if (age != person.age) { return false; } return name != null ? name.equals(person.name) : person.name == null; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof Person)) { // 涉及繼承,使得與子類(lèi)之間也可以判斷,問(wèn)題 2 后半的寫(xiě)法 return false; } Person person = (Person) o; if (age != person.age) { return false; } return name != null ? name.equals(person.name) : person.name == null; } }Scala
scala 中編寫(xiě)的方式大致相同,但是結(jié)合其語(yǔ)法,相比似乎又簡(jiǎn)單又繁瑣。
簡(jiǎn)單是指當(dāng)沒(méi)有子類(lèi),或和子類(lèi)判斷一定為 false 時(shí)(違反LSP),可以這樣寫(xiě):
class Person(val name: String, val age: Int) { override def equals(other: Any): Boolean = other match { // 問(wèn)題 1 的寫(xiě)法 case that: this.getClass == that.getClass && Person => name == that.name && age == that.age case _ => false } }
繁瑣是指假如這時(shí)出現(xiàn)了一個(gè)子類(lèi) Student 且增加了一個(gè)域 sid,假如我們需要兩個(gè)類(lèi)可相互判斷,則上述方法在判斷一個(gè) Person 對(duì)象和一個(gè) Student 對(duì)象時(shí)一定會(huì)返回 false。
因此《Programming in Scala》中建議采用如下的編寫(xiě)方式:
class Person(val name: String, val age: Int) { def canEqual(other: Any): Boolean = other.isInstanceOf[Person] override def equals(other: Any): Boolean = other match { // 問(wèn)題 2 的寫(xiě)法 case that: Person => (that canEqual this) && name == that.name && age == that.age case _ => false } } class Student(override val name: String, override val age: Int, val sid: Int) extends Person(name, age){ }
上面 canEqual 方法的作用和 Java 代碼中判斷 instanceof 的作用是一致的,但比 Java 中的判斷更加靈活,比如可以限定不同子類(lèi)與父類(lèi)的判斷關(guān)系。
比如有一個(gè) Person 的子類(lèi) Teacher,我們希望它只能和 Teacher 類(lèi)進(jìn)行判斷,與 Person 和 Student 判斷都返回 false,該如何寫(xiě)呢?一種錯(cuò)誤的寫(xiě)法如下:
class Teacher(override val name: String, override val age: Int, val tid: Int) extends Person(name, age){ override def equals(other: Any): Boolean = other match { case that: Teacher => this.getClass == that.getClass && name == that.name && age == that.age case _ => false } } val s1 = new Student("z", 1, 2) val t1 = new Teacher("z", 1, 2) println(s1 == t1) // true println(t1 == s1) // false 違反了對(duì)稱(chēng)性
正確的寫(xiě)法應(yīng)該是:
class Teacher(override val name: String, override val age: Int, val tid: Int) extends Person(name, age){ override def canEqual(other: Any): Boolean = other.isInstanceOf[Teacher] override def equals(other: Any): Boolean = other match { // 問(wèn)題 3 的寫(xiě)法 case that: Teacher => super.equals(that) && (that canEqual this) && name == that.name && age == that.age && tid == that.tid case _ => false } }
注意只覆蓋了 canEqual 方法也會(huì)違反對(duì)稱(chēng)性。在 Java 中要實(shí)現(xiàn)相同的效果,則也需要編寫(xiě)類(lèi)似的 canEqual 方法,就留給讀者自己考慮了。
總之,在編寫(xiě)單個(gè)類(lèi)的 equals 方法時(shí)比較簡(jiǎn)單,當(dāng)涉及子類(lèi)繼承時(shí),就要多考慮一下了。
另外不要忘記覆蓋 hashcode 方法哦。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/68055.html
摘要:本章我們來(lái)學(xué)習(xí)一下的基本數(shù)據(jù)類(lèi)型與類(lèi)型系統(tǒng)。字符串就是一個(gè)抽象數(shù)據(jù)類(lèi)型。如果程序語(yǔ)言的語(yǔ)法中含有類(lèi)型標(biāo)記,就稱(chēng)該語(yǔ)言是顯式類(lèi)型化的,否則就稱(chēng)為隱式類(lèi)型化的。但是,可以把中對(duì)應(yīng)的這幾種基本數(shù)據(jù)類(lèi)型,理解為的基本類(lèi)型的裝箱類(lèi)。 第4章 基本數(shù)據(jù)類(lèi)型與類(lèi)型系統(tǒng) 《Kotlin極簡(jiǎn)教程》正式上架: 點(diǎn)擊這里 > 去京東商城購(gòu)買(mǎi)閱讀 點(diǎn)擊這里 > 去天貓商城購(gòu)買(mǎi)閱讀 非常感謝您親愛(ài)的讀...
摘要:提供了類(lèi)型推導(dǎo)來(lái)解決這個(gè)問(wèn)題。函數(shù)式語(yǔ)言里比較經(jīng)典的類(lèi)型推導(dǎo)的方法是,并且它是在里首先使用的。的類(lèi)型推導(dǎo)有一點(diǎn)點(diǎn)不同,不過(guò)思想上是一致的推導(dǎo)所有的約束條件,然后統(tǒng)一到一個(gè)類(lèi)型上。而推導(dǎo)器是所有類(lèi)型推導(dǎo)器的基礎(chǔ)。 Scala類(lèi)型推導(dǎo) 之劍 2016.5.1 00:38:12 類(lèi)型系統(tǒng) 什么是靜態(tài)類(lèi)型?為什么它們很有用? 根據(jù)Picrce的說(shuō)法:類(lèi)型系統(tǒng)是一個(gè)可以根據(jù)代碼段計(jì)算出來(lái)的值對(duì)...
摘要:框架官方支持的框架,風(fēng)格頗為類(lèi)似,并且充分發(fā)揮了的強(qiáng)類(lèi)型優(yōu)勢(shì)。這是一個(gè)主要面向的框架,為提供了一些額外特性。依賴(lài)注入框架用法簡(jiǎn)單,支持等特性。 首先要說(shuō)明,Kotlin支持你所知道的所有Java框架和庫(kù),包括但不限于Spring全家桶、Guice、Hibernate、MyBatis、Jackson等,甚至有人在用Kotlin寫(xiě)Spark大數(shù)據(jù)程序,因此Kotlin不需要專(zhuān)門(mén)的框架。因此...
摘要:原文 Introduction (Business Case) Lambda expressions are a new and important feature included in Java SE 8. A lambda expression provides a way to represent one method interface using an expression...
閱讀 5479·2021-11-25 09:43
閱讀 1746·2021-10-27 14:18
閱讀 1112·2021-09-22 16:03
閱讀 1406·2019-08-30 13:19
閱讀 1624·2019-08-30 11:15
閱讀 1760·2019-08-26 14:04
閱讀 3182·2019-08-23 18:40
閱讀 1216·2019-08-23 18:17