摘要:總之,在編寫單個類的方法時比較簡單,當涉及子類繼承時,就要多考慮一下了。另外不要忘記覆蓋方法哦。
相信讀過 《Effective Java》 的讀者都已經知道編寫 equals 方法的作用與重要性,基本概念不多做解釋,這里就總結一下如何編寫正確的 equals 方法。
equals 在 Java 和 Scala 中含義相同,都需要滿足以下五個條件:
自反性
對稱性
傳遞性
一致性
anyObject.equals(null) == false
現在我們有三個問題:
假如我們只有一個類 Person,如何寫?
假如 Person 類有一個子類 Student,相互不能判斷(一定返回 false),如何寫?相互可以判斷,如何寫?
假如 Person 和 Student 可以相互判斷,但另一子類 Teacher 只能和同類判斷,如何寫?
Java《Effective Java》 中最后推薦的寫法步驟是:
通過 == 判斷是否是同一個對象
用 instanceof 判斷是否是正確的類型,注意這里已經包含了 null 的情況,所以不用多帶帶另寫
將對象轉換成正確的類型
對需要判斷的域分別進行對比
需要注意,基本類型用 == 判斷,例外是 float 用 Float.compare,double 用 Double.compare,因為有 NaN 等特殊值存在。
上述第二步中還有另一個變種,是使用 getClass 進行類型判斷,這樣的話只有類型完全一致才能返回 true,如果只是單一的類還好,要是涉及類之間的繼承,則違背了 Liskov Substitution Principle,所以最后書中的結論是:
There is no way to extend an instantiable class and add a value component while preserving the equals contract.
由于現在的 IDE 例如 IntelliJ IDEA 已經可以自動為我們生成 equals 方法,還可以選擇是否允許子類判斷,是否可為 null 等判斷,所以我們就不必手動編寫了,但是生成的結果也是符合上面的 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()) { // 不涉及繼承,問題 1 和 問題 2 前半的寫法 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)) { // 涉及繼承,使得與子類之間也可以判斷,問題 2 后半的寫法 return false; } Person person = (Person) o; if (age != person.age) { return false; } return name != null ? name.equals(person.name) : person.name == null; } }Scala
scala 中編寫的方式大致相同,但是結合其語法,相比似乎又簡單又繁瑣。
簡單是指當沒有子類,或和子類判斷一定為 false 時(違反LSP),可以這樣寫:
class Person(val name: String, val age: Int) { override def equals(other: Any): Boolean = other match { // 問題 1 的寫法 case that: this.getClass == that.getClass && Person => name == that.name && age == that.age case _ => false } }
繁瑣是指假如這時出現了一個子類 Student 且增加了一個域 sid,假如我們需要兩個類可相互判斷,則上述方法在判斷一個 Person 對象和一個 Student 對象時一定會返回 false。
因此《Programming in Scala》中建議采用如下的編寫方式:
class Person(val name: String, val age: Int) { def canEqual(other: Any): Boolean = other.isInstanceOf[Person] override def equals(other: Any): Boolean = other match { // 問題 2 的寫法 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 中的判斷更加靈活,比如可以限定不同子類與父類的判斷關系。
比如有一個 Person 的子類 Teacher,我們希望它只能和 Teacher 類進行判斷,與 Person 和 Student 判斷都返回 false,該如何寫呢?一種錯誤的寫法如下:
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 違反了對稱性
正確的寫法應該是:
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 { // 問題 3 的寫法 case that: Teacher => super.equals(that) && (that canEqual this) && name == that.name && age == that.age && tid == that.tid case _ => false } }
注意只覆蓋了 canEqual 方法也會違反對稱性。在 Java 中要實現相同的效果,則也需要編寫類似的 canEqual 方法,就留給讀者自己考慮了。
總之,在編寫單個類的 equals 方法時比較簡單,當涉及子類繼承時,就要多考慮一下了。
另外不要忘記覆蓋 hashcode 方法哦。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/68055.html
摘要:本章我們來學習一下的基本數據類型與類型系統。字符串就是一個抽象數據類型。如果程序語言的語法中含有類型標記,就稱該語言是顯式類型化的,否則就稱為隱式類型化的。但是,可以把中對應的這幾種基本數據類型,理解為的基本類型的裝箱類。 第4章 基本數據類型與類型系統 《Kotlin極簡教程》正式上架: 點擊這里 > 去京東商城購買閱讀 點擊這里 > 去天貓商城購買閱讀 非常感謝您親愛的讀...
摘要:框架官方支持的框架,風格頗為類似,并且充分發揮了的強類型優勢。這是一個主要面向的框架,為提供了一些額外特性。依賴注入框架用法簡單,支持等特性。 首先要說明,Kotlin支持你所知道的所有Java框架和庫,包括但不限于Spring全家桶、Guice、Hibernate、MyBatis、Jackson等,甚至有人在用Kotlin寫Spark大數據程序,因此Kotlin不需要專門的框架。因此...
摘要:原文 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...
閱讀 4983·2021-11-25 09:43
閱讀 1685·2021-10-27 14:18
閱讀 1057·2021-09-22 16:03
閱讀 1349·2019-08-30 13:19
閱讀 1572·2019-08-30 11:15
閱讀 1645·2019-08-26 14:04
閱讀 3124·2019-08-23 18:40
閱讀 1166·2019-08-23 18:17