目錄
不覆寫equals方法" >不覆寫equals方法
覆寫equals方法" >覆寫equals方法
集合中equals方法的等價關係" >集合中equals方法的等價關係
如何寫equals方法" >如何寫equals方法
關於實作的equals需要注意的問題" >關於實作的equals需要注意的問題
首頁 Java java教程 關於java覆蓋equals更深層的方法概述

關於java覆蓋equals更深層的方法概述

Apr 24, 2017 pm 04:07 PM

最近和同事談到equals和==的差別。這其實是個非常老套簡單的問題,但當你要親自覆蓋equals方法時,才發現,有一些你不知道卻又不得不知道的事。覆蓋equals,講究很多。儘管Object是一個很具體的類,但他的主要作用還是為了擴展。他的所有非final方法都有明確的通用約定。因為他們被設計成要被覆蓋的方法。任何一個類,在覆寫equals、hashCode、toString、clone、finalize時,都有責任遵守這些方法的通用約定。如果不能做到這一點,那麼當多個類別組合時將難以發揮我們期望的效果。

不覆寫equals方法


覆寫equals方法看起來簡單,但是有許多覆寫方法會導致錯誤。最容易避免這種錯誤的方法就是不覆寫equals,這種情況下每個類別的實例都只與自身相等。那麼在什麼情況下我們可以選擇不覆寫equals方法呢?

類別的每個實例本質上是唯一的

對於代表活動實體而不是值的類別確實如此,例如每個執行緒實例。我們用equals方法比較他是毫無意義的,因為每個線程都是唯一的。在這種情況下,我們不用覆寫equals方法,因為Object類別中的equals已經完全夠用了。

Object類別中equals方法實作:

 public boolean equals(Object obj) { return (this == obj);  }
登入後複製
登入後複製

#不關心類別是否需要邏輯相等的判斷

有些類別是一些“數值類別”,比較大小,數學運算是這些類別的本職工作。在這種情況下,需要我們將類別中存放的數值進行比較,需要進行邏輯相等的判斷。除此之外的類,很大部分類別沒有「一個是否等於另外一個」的概念。這種不關心邏輯相等的類別不需要覆寫equals方法。

超類別實作的equals對子類別適用

舉個例子,繼承了AbstractSet類別的HashSet類別在equals方法上並沒有任何區別,那麼HashSet直接使用AbstractSet的equals方法即可。

覆寫equals方法

與上面的內容相反的,我們需要覆寫equals方法的情況就是:如果類別具有自己的邏輯相等的概念,並且父類沒有進行可用的equals方法覆蓋。這時需要我們親自進行覆蓋。

集合中equals方法的等價關係


equals方法實現了等價關係。離散數學中學過等價關係的概念,對於一個R上的二元關係,如果它滿足自反對稱和傳遞,那麼他就是等價的。讓我們來具體分析一下equals和這三種性質的關係。

自反性 : 對於任何非null的參考值x,x.equals(x)必須傳回true

對稱性 : 對於任何非null的參考值x和y,當且僅當y.equals(x)回傳true時,x.equals(y)必定傳回true

傳遞性 : 對於任何非null的參考值x、y和z,如果x.equals(y)回傳true、y.equals(z)也回傳true,那麼x.equals(z)必定回傳true。

自反:∀ a ∈A, => (a, a) ∈ R
對稱性:(a, b) ∈R∧ a ≠ b => (b, a) ∈R
傳遞性:(a, b)∈R,(b, c)∈R =>(a, c)∈R

對比這三個性質,沒有任何問題。由此可得,equals方法實現了等價關係。

如何寫equals方法


Object的equals方法只是簡單的看看位址,這顯然不可能滿足我們的要求。那麼在自己編寫equals方法進行覆蓋時如何保證編寫出高品質的,邏輯比較的方法呢? equals的編寫可以概括為下面四個步驟:

1.使用==運算子檢查參數是否只是物件的參考
如果結果相等則傳回true,說明x與y是一個物件的不同引用,不需要再進行判斷了。

2.使用instanceof運算子檢查參數是否類型正確
如果結果不是正確的類型則傳回false,因為我們的equals方法是繼承自Object類別的,所以參數的型別無法避免的是Object,我們先使用instanceof對參數做型別判斷,如果型別都不正確,就不用進行下一步判斷了。

3.把參數轉換成正確的型別
因為之前做了偵測,所以這一步的型別轉換沒有問題。

4.對每個類別中需要邏輯比較的域值進行判斷
已經確保x和y是相同類型的不同實例,將需要判斷的邏輯比較的域值取出進行比較判斷即可。如果全部正確則回傳true,否則傳回false。

關於實作的equals需要注意的問題


當寫完equals方法後,一定要反覆的判斷是否符合自反性,對稱性和傳遞性。不僅僅如此,在確保等價的情況下,編寫equals方法時還有一些值得注意的事情,我們需要以此改進。

覆寫equals方法時總是要覆寫hashCode方法
如果我們在寫有關雜湊的類別時,必須在覆寫equals方法時覆寫hashCode方法。因為在散列表中,邏輯相同的物件應該具有相同的雜湊碼。舉個比較簡單的例子:將String存入HashSet,有可能兩個內容相同的String字串用==判斷為false,但是他們在HashSet中只存在了一份。這就是因為邏輯相同的String擁有這相同的hashCode。
普遍性的,如果你為你的類別覆寫了equals方法,那麼證明在某種情況下會有兩個不同物件是邏輯相等的。此時如果與雜湊相關,那麼這兩個物件需要相同的hashCode。所以覆寫equals方法時總是要覆寫hashCode方法。

不要讓equals方法過於智慧
如果我們只是簡單的按照上面的實作流程來寫equals的方法,既符合規定也不會導致奇怪的錯誤。但如果我們非要去追求各種、花俏的等價關係,而把程式碼搞得臃腫不堪,那麼既違反了高內聚的初衷,也會讓程式碼出一些莫名其妙的錯誤。

不要將equals方法的參數類型弄錯
說出來可能感覺好笑,但這確實是會發生的情況。修改了參數類型之後的equals方法已完全於Object類別沒有了關係,編譯器不會報錯,留下的只是給程式設計師無盡的頭痛。如果無法意識到參數類型是Object,很有可能花幾個小時也搞不清楚程式為什麼不能正常運作。

#

最近和同事談到equals和==的差別。這其實是個非常老套簡單的問題,但當你要親自覆蓋equals方法時,才發現,有一些你不知道卻又不得不知道的事。覆蓋equals,講究很多。儘管Object是一個很具體的類,但他的主要作用還是為了擴展。他的所有非final方法都有明確的通用約定。因為他們被設計成要被覆蓋的方法。任何一個類,在覆寫equals、hashCode、toString、clone、finalize時,都有責任遵守這些方法的通用約定。如果不能做到這一點,那麼當多個類別組合時將難以發揮我們期望的效果。

不覆寫equals方法


覆寫equals方法看起來簡單,但是有許多覆寫方法會導致錯誤。最容易避免這種錯誤的方法就是不覆寫equals,這種情況下每個類別的實例都只與自身相等。那麼在什麼情況下我們可以選擇不覆寫equals方法呢?

類別的每個實例本質上是唯一的

對於代表活動實體而不是值的類別確實如此,例如每個執行緒實例。我們用equals方法比較他是毫無意義的,因為每個線程都是唯一的。在這種情況下,我們不用覆寫equals方法,因為Object類別中的equals已經完全夠用了。

Object類別中equals方法實作:

 public boolean equals(Object obj) { return (this == obj);  }
登入後複製
登入後複製

#不關心類別是否需要邏輯相等的判斷

有些類別是一些“數值類別”,比較大小,數學運算是這些類別的本職工作。在這種情況下,需要我們將類別中存放的數值進行比較,需要進行邏輯相等的判斷。除此之外的類,很大部分類別沒有「一個是否等於另外一個」的概念。這種不關心邏輯相等的類別不需要覆寫equals方法。

超類別實作的equals對子類別適用

舉個例子,繼承了AbstractSet類別的HashSet類別在equals方法上並沒有任何區別,那麼HashSet直接使用AbstractSet的equals方法即可。

覆寫equals方法

與上面的內容相反的,我們需要覆寫equals方法的情況就是:如果類別具有自己的邏輯相等的概念,並且父類沒有進行可用的equals方法覆蓋。這時需要我們親自進行覆蓋。

集合中equals方法的等價關係


equals方法實現了等價關係。離散數學中學過等價關係的概念,對於一個R上的二元關係,如果它滿足自反對稱和傳遞,那麼他就是等價的。讓我們來具體分析一下equals和這三種性質的關係。

自反性 : 對於任何非null的參考值x,x.equals(x)必須傳回true

對稱性 : 對於任何非null的參考值x和y,當且僅當y.equals(x)回傳true時,x.equals(y)必定傳回true

傳遞性 : 對於任何非null的參考值x、y和z,如果x.equals(y)回傳true、y.equals(z)也回傳true,那麼x.equals(z)必定回傳true。

自反:∀ a ∈A, => (a, a) ∈ R
對稱性:(a, b) ∈R∧ a ≠ b => (b, a) ∈R
傳遞性:(a, b)∈R,(b, c)∈R =>(a, c)∈R

對比這三個性質,沒有任何問題。由此可得,equals方法實現了等價關係。

如何寫equals方法


Object的equals方法只是簡單的看看位址,這顯然不可能滿足我們的要求。那麼在自己編寫equals方法進行覆蓋時如何保證編寫出高品質的,邏輯比較的方法呢? equals的編寫可以概括為下面四個步驟:

1.使用==運算子檢查參數是否只是物件的參考
如果結果相等則傳回true,說明x與y是一個物件的不同引用,不需要再進行判斷了。

2.使用instanceof運算子檢查參數是否類型正確
如果結果不是正確的類型則傳回false,因為我們的equals方法是繼承自Object類別的,所以參數的型別無法避免的是Object,我們先使用instanceof對參數做型別判斷,如果型別都不正確,就不用進行下一步判斷了。

3.把參數轉換成正確的型別
因為之前做了偵測,所以這一步的型別轉換沒有問題。

4.對每個類別中需要邏輯比較的域值進行判斷
已經確保x和y是相同類型的不同實例,將需要判斷的邏輯比較的域值取出進行比較判斷即可。如果全部正確則回傳true,否則傳回false。

關於實作的equals需要注意的問題


當寫完equals方法後,一定要反覆的判斷是否符合自反性,對稱性和傳遞性。不僅僅如此,在確保等價的情況下,編寫equals方法時還有一些值得注意的事情,我們需要以此改進。

覆寫equals方法時總是要覆寫hashCode方法
如果我們在寫有關雜湊的類別時,必須在覆寫equals方法時覆寫hashCode方法。因為在散列表中,邏輯相同的物件應該具有相同的雜湊碼。舉個比較簡單的例子:將String存入HashSet,有可能兩個內容相同的String字串用==判斷為false,但是他們在HashSet中只存在了一份。這就是因為邏輯相同的String擁有這相同的hashCode。
普遍性的,如果你為你的類別覆寫了equals方法,那麼證明在某種情況下會有兩個不同物件是邏輯相等的。此時如果與雜湊相關,那麼這兩個物件需要相同的hashCode。所以覆寫equals方法時總是要覆寫hashCode方法。

不要讓equals方法過於智慧
如果我們只是簡單的按照上面的實作流程來寫equals的方法,既符合規定也不會導致奇怪的錯誤。但如果我們非要去追求各種、花俏的等價關係,而把程式碼搞得臃腫不堪,那麼既違反了高內聚的初衷,也會讓程式碼出一些莫名其妙的錯誤。

不要將equals方法的參數類型弄錯
說出來可能感覺好笑,但這確實是會發生的情況。修改了參數類型之後的equals方法已完全於Object類別沒有了關係,編譯器不會報錯,留下的只是給程式設計師無盡的頭痛。如果無法意識到參數類型是Object,很有可能花幾個小時也搞不清楚程式為什麼不能正常運作。

以上是關於java覆蓋equals更深層的方法概述的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

突破或從Java 8流返回? 突破或從Java 8流返回? Feb 07, 2025 pm 12:09 PM

Java 8引入了Stream API,提供了一種強大且表達力豐富的處理數據集合的方式。然而,使用Stream時,一個常見問題是:如何從forEach操作中中斷或返回? 傳統循環允許提前中斷或返回,但Stream的forEach方法並不直接支持這種方式。本文將解釋原因,並探討在Stream處理系統中實現提前終止的替代方法。 延伸閱讀: Java Stream API改進 理解Stream forEach forEach方法是一個終端操作,它對Stream中的每個元素執行一個操作。它的設計意圖是處

PHP:網絡開發的關鍵語言 PHP:網絡開發的關鍵語言 Apr 13, 2025 am 12:08 AM

PHP是一種廣泛應用於服務器端的腳本語言,特別適合web開發。 1.PHP可以嵌入HTML,處理HTTP請求和響應,支持多種數據庫。 2.PHP用於生成動態網頁內容,處理表單數據,訪問數據庫等,具有強大的社區支持和開源資源。 3.PHP是解釋型語言,執行過程包括詞法分析、語法分析、編譯和執行。 4.PHP可以與MySQL結合用於用戶註冊系統等高級應用。 5.調試PHP時,可使用error_reporting()和var_dump()等函數。 6.優化PHP代碼可通過緩存機制、優化數據庫查詢和使用內置函數。 7

PHP與Python:了解差異 PHP與Python:了解差異 Apr 11, 2025 am 12:15 AM

PHP和Python各有優勢,選擇應基於項目需求。 1.PHP適合web開發,語法簡單,執行效率高。 2.Python適用於數據科學和機器學習,語法簡潔,庫豐富。

PHP與其他語言:比較 PHP與其他語言:比較 Apr 13, 2025 am 12:19 AM

PHP適合web開發,特別是在快速開發和處理動態內容方面表現出色,但不擅長數據科學和企業級應用。與Python相比,PHP在web開發中更具優勢,但在數據科學領域不如Python;與Java相比,PHP在企業級應用中表現較差,但在web開發中更靈活;與JavaScript相比,PHP在後端開發中更簡潔,但在前端開發中不如JavaScript。

Java程序查找膠囊的體積 Java程序查找膠囊的體積 Feb 07, 2025 am 11:37 AM

膠囊是一種三維幾何圖形,由一個圓柱體和兩端各一個半球體組成。膠囊的體積可以通過將圓柱體的體積和兩端半球體的體積相加來計算。本教程將討論如何使用不同的方法在Java中計算給定膠囊的體積。 膠囊體積公式 膠囊體積的公式如下: 膠囊體積 = 圓柱體體積 兩個半球體體積 其中, r: 半球體的半徑。 h: 圓柱體的高度(不包括半球體)。 例子 1 輸入 半徑 = 5 單位 高度 = 10 單位 輸出 體積 = 1570.8 立方單位 解釋 使用公式計算體積: 體積 = π × r2 × h (4

PHP與Python:核心功能 PHP與Python:核心功能 Apr 13, 2025 am 12:16 AM

PHP和Python各有優勢,適合不同場景。 1.PHP適用於web開發,提供內置web服務器和豐富函數庫。 2.Python適合數據科學和機器學習,語法簡潔且有強大標準庫。選擇時應根據項目需求決定。

創造未來:零基礎的 Java 編程 創造未來:零基礎的 Java 編程 Oct 13, 2024 pm 01:32 PM

Java是熱門程式語言,適合初學者和經驗豐富的開發者學習。本教學從基礎概念出發,逐步深入解說進階主題。安裝Java開發工具包後,可透過建立簡單的「Hello,World!」程式來實踐程式設計。理解程式碼後,使用命令提示字元編譯並執行程序,控制台上將輸出「Hello,World!」。學習Java開啟了程式設計之旅,隨著掌握程度加深,可創建更複雜的應用程式。

PHP:許多網站的基礎 PHP:許多網站的基礎 Apr 13, 2025 am 12:07 AM

PHP成為許多網站首選技術棧的原因包括其易用性、強大社區支持和廣泛應用。 1)易於學習和使用,適合初學者。 2)擁有龐大的開發者社區,資源豐富。 3)廣泛應用於WordPress、Drupal等平台。 4)與Web服務器緊密集成,簡化開發部署。

See all articles