轉載自 這裡
前言 :
Scala 是一個可直譯、編譯、靜態、運行於 JVM 之上、可與 Java 互操作、融合物件導向編程特性與函式編程風格的程式語言. Scala 的繼承作了一些限制,這使你在使用繼承前必須多一份思考.
重新定義 equals() 方法 :
如果你要重新定義 equals(),必須注意幾個地方,例如,你可能如下定義了 equals() 方法 :
不是說重新定義 equals() 實作物件相等性比較,再使用 == 就可以測試物件的實質相等性嗎?但上例中,equals() 的結果是 true,但 == 的結果是false?原因在於,你沒有重新定義繼承下來的 equals(),因為你另外定義了一個接受 Point 型態的 equals() 方法! 事實上,在 Scala 中,重新定義一定要加上 override 關鍵字,以確保你確實重新定義了父類別的某個方法,上例中沒有加上 override,而編譯器沒提出錯誤訊息時,你就要知道你並沒有重新定義父類別的 equals() 方法, 來看看真正有重新定義 equals() 的版本 :
這邊用到了 模式比對(Pattern match)中變數模式(Variable pattern)的語法,雖然還沒正式談到,不過並不難,match 語法中會嘗試看看傳入的 a 是否可以被Point 型態所宣告 that 變數參考,如果可以 讓 that 參考至 a 所參考的物件,然後執行 => 之後的程式碼.p1 與 p2 座標都是同一點,所以實際上指的相同的座標,使用 == 測試的結果也是true了,但是 HashSet 中放入的 p1 與要測試的 p2 明明是指同一點,為什麼 contains 測試會有可能顯示false? 因為你在重新定義 equals() 時,並沒有重新定義 hashCode,在許多場合,例如將物件加入群集 (Collection)時,會同時利用 equals() 與 hashCode() 來判斷是否加入的是(實質上)相同的物件. 來看看定義 hashCode 時必須遵守的約定(取自 java.lang.Object 的hashCode() 說明 ):
以 HashSet 為例,會先使用 hashCode 得出該將物件放至哪個雜湊桶(hash buckets)中,如果雜湊桶有物件,再進一步使用 equals() 確定實質相等性,從而確定 Set 中不會有重複的物件。上例中說可能會顯示 false,是因為若湊巧物件 hashCode 算出在同一個雜湊桶,再進一步用 equals() 就有可能出現 true. 在重新定義 equals() 時,最好重新一併重新定義 hashCode. 例如 :
一個重要的觀念是,定義 equals() 與 hashCode 時,最好別使用狀態會改變的資料成員. 你可能會想,以這個例子來說,點會移動,如果移動了就不是相同的點了,不是嗎? 假設 x、y 是個允許會變動的成員,那麼就會發生這個情況 :
明 明是記憶體中同一個物件,但置入 HashSet 後,最後跟我說不包括 p1?這是因為,你改變了x,算出來的 hashCode 也就改變了,使用 contains() 嘗試比對時,會看看新算出來的雜湊桶中是不是有物件,而根本不是置入 p1 的雜湊桶中尋找,結果就是 false了. 再來看看在實作 equals() 時要遵守的約定(取自 java.lang.Object 的 equals() 說明 ) :
目前定義的 Point,其 equals() 方法滿足以上幾個約定(你可以自行寫程式測試)。現在考慮繼承的情況,你要定義 3D 的點 :
結 果該是 true 或 false 需要討論一下。3D 的點與 2D 的點是否相等呢?假設你考慮的是點投射在 xy 平面上是否相等,那 p1 == p2 為 true就可以接受,在此假設之下,再來看 p2 == p1 為false,這違反 equals() 對稱性的對稱性合約。如果你要滿足對稱性,則要作個修改 :
p1 等於 p2,p2 等於 p1,這符合對稱性合約了。但 p2 等於 p1,p1 等於 p3,但 p2 不等於 p3,這違反傳遞性合約。問題點在於,2D 的點並沒有 z 軸資訊,無論如何也沒辦法滿足傳遞性了. 一般來說,對於不同的類別實例,會將之視為不同,基本上你可以這麼設計 :
直接判斷類別,讓不同類別的實例視為不相等,就這個例子而言,使得 Point 只能與 Point 比,Point3D 只能與 Point3D 比,直接解決了不同繼承階層下 equals() 的合約問題. 不過在以下這種需求時,這樣的定義也許不符合你的需求 :
你也許是在某處建立了個匿名類別物件,然後在程式中某處又打算測試看看與 p1 是否相等,但結果並不是顯示 true,這是因為你嚴格地在 equals() 中檢查了實例的類別名稱. 你可以將定義改為以下 :
在 equals() 中,你不僅檢查傳入的實例是否為 Point,也反過來讓傳入的實例取得 this 的型態進行測試(這是 Visitor 模式 的實現)。如果 p1 是 Point 物件,而 p2 是 Point3D 物件,p1.equals(p2) 時,由於傳入的實例可以取得 this 的型態進行測試,p2 反過來測試 p1 是不是Point3D,結果不是,所以 equals() 傳回false,利用這個方式,讓有具體名稱的子類別實例,不會與父類別實例有相等成立的可能性。如果是直接繼承 Point 類別的匿名類別物件,則直接繼承 canEquals() 方法,由於匿名類別物件還是一種 Point 實例,因此equals() 的結果會是 true.
This is a blog to track what I had learned and share knowledge with all who can take advantage of them
標籤
- [ 英文學習 ]
- [ 計算機概論 ]
- [ 深入雲計算 ]
- [ 雜七雜八 ]
- [ Algorithm in Java ]
- [ Data Structures with Java ]
- [ IR Class ]
- [ Java 文章收集 ]
- [ Java 代碼範本 ]
- [ Java 套件 ]
- [ JVM 應用 ]
- [ LFD Note ]
- [ MangoDB ]
- [ Math CC ]
- [ MongoDB ]
- [ MySQL 小學堂 ]
- [ Python 考題 ]
- [ Python 常見問題 ]
- [ Python 範例代碼 ]
- [心得扎記]
- [網路教學]
- [C 常見考題]
- [C 範例代碼]
- [C/C++ 範例代碼]
- [Intro Alg]
- [Java 代碼範本]
- [Java 套件]
- [Linux 小技巧]
- [Linux 小學堂]
- [Linux 命令]
- [ML In Action]
- [ML]
- [MLP]
- [Postgres]
- [Python 學習筆記]
- [Quick Python]
- [Software Engineering]
- [The python tutorial]
- 工具收集
- 設計模式
- 資料結構
- ActiveMQ In Action
- AI
- Algorithm
- Android
- Ansible
- AWS
- Big Data 研究
- C/C++
- C++
- CCDH
- CI/CD
- Coursera
- Database
- DB
- Design Pattern
- Device Driver Programming
- Docker
- Docker 工具
- Docker Practice
- Eclipse
- English Writing
- ExtJS 3.x
- FP
- Fraud Prevention
- FreeBSD
- GCC
- Git
- Git Pro
- GNU
- Golang
- Gradle
- Groovy
- Hadoop
- Hadoop. Hadoop Ecosystem
- Java
- Java Framework
- Java UI
- JavaIDE
- JavaScript
- Jenkins
- JFreeChart
- Kaggle
- Kali/Metasploit
- Keras
- KVM
- Learn Spark
- LeetCode
- Linux
- Lucene
- Math
- ML
- ML Udemy
- Mockito
- MPI
- Nachos
- Network
- NLP
- node js
- OO
- OpenCL
- OpenMP
- OSC
- OSGi
- Pandas
- Perl
- PostgreSQL
- Py DS
- Python
- Python 自製工具
- Python Std Library
- Python tools
- QEMU
- R
- Real Python
- RIA
- RTC
- Ruby
- Ruby Packages
- Scala
- ScalaIA
- SQLAlchemy
- TensorFlow
- Tools
- UML
- Unix
- Verilog
- Vmware
- Windows 技巧
- wxPython
訂閱:
張貼留言 (Atom)
[Git 常見問題] error: The following untracked working tree files would be overwritten by merge
Source From Here 方案1: // x -----删除忽略文件已经对 git 来说不识别的文件 // d -----删除未被添加到 git 的路径中的文件 // f -----强制运行 # git clean -d -fx 方案2: 今天在服务器上 gi...
-
前言 : 為什麼程序管理這麼重要呢?這是因為: * 首先,本章一開始就談到的,我們在操作系統時的各項工作其實都是經過某個 PID 來達成的 (包括你的 bash 環境), 因此,能不能進行某項工作,就與該程序的權限有關了。 * 再來,如果您的 Linux 系統是個...
-
屬性 : 系統相關 - 檔案與目錄 語法 : du [參數] [檔案] 參數 | 功能 -a | 顯示目錄中個別檔案的大小 -b | 以bytes為單位顯示 -c | 顯示個別檔案大小與總和 -D | 顯示符號鏈結的來源檔大小 -h | Hum...
-
來源自 這裡 說明 : split 是 Perl 中非常有用的函式之一,它可以將一個字串分割並將之置於陣列中。若無特別的指定,該函式亦使用 RE 與 $_ 變數 語法 : * split /PATTERN/,EXPR,LIMIT * split /...
沒有留言:
張貼留言