前言 :
Scala 是一個可直譯、編譯、靜態、運行於 JVM 之上、可與 Java 互操作、融合物件導向編程特性與函式編程風格的程式語言. Scala 的繼承作了一些限制,這使你在使用繼承前必須多一份思考.
物件相等性 :
在 Scala 中,如果要比較兩個物件的實質相等性,可以使用 == 或 !=,例如 :
- val s1 = new String("Java")
- val s2 = new String("Java")
- println(s1 == s2) // 顯示 true
- println(s1 eq s2) // 顯示 false
在 API 文件說明中表示,== 的作用等於 equals() 的作用 :
equals() 的作用是測試兩個 args0 的作用與 this 是否參考同一物件. 而 != 等於 == 的反相結果,也就是 o != arg0 等於 !(o == (arg0)). 事實上,當你在 Scala 中定義一個類別時沒有明確指定父類別,則會繼承 scala.AnyRef(相當於 Java 的 java.lang.Object),這是 Any 的直接子類別。AnyRef 定義了== 與 !=,其版本為 :
!=等於==的反相結果,而==的定義內容相當於 :
- final def == (arg0 : Any): Boolean =
- if (this eq null) arg0 eq null else this.equals(arg0)
ne 為 eq 的反相結果,而 eq 主要在測試目前物件與 arg0 所參考的物件是否為同一物件. 無論是 Any 中的 ==、!= 或 AnyRef 中的 ==、!=、eq、ne 方法,都被宣告為 final,你沒辦法在子類別中重新定義(AnyRef 被宣告為 final,你沒辦法繼承),別以為以下是重新定義 == 方法 :
- class Point(val x: Int, val y: Int) {
- def ==(that: Point) = this.x == that.x && this.y == that.y
- }
- val p1 = new Point(1, 1)
- val p2 = new Point(1, 1)
- println(p1 == p2) // 顯示 true
事實上是,你沒有重新定義 Any 或 AnyRef f的 == 方法,你是定義了一個新的 == 方法,而直接繼承了 Any 與 AnyRef f的 == 方法,所以之前顯示 true 是因為方法被重載了,而你使用的是接受 Point 參數的 == 版本. 因為 Any 與 AnyRef 的 ==、!= 都被宣告為 final,你不能重新定義 ==、!= 方法來定義自己的物件相等性。依以上的說明,無論是哪個版本,!= 一定是 == 的反相結果,而 == 都會呼叫 equals() 方法,所以結論是,要定義物件相等性,請重新定義 equals() 方法(跟 Java 相同).
如何正確定義 equals() 需要作些討論,如果你熟悉 Java,可以先看看 Java 物件相等性 中的說明,如果你想要知道如何以 Scala 語法正確定義 equals() 方法,則可以看看 重新定義 equals() 方法.
補充說明 :
* [ Java 小學堂 ] Java 世界裡 equals 與 == 的差別