基于价值的类
在Java 8中,某些類在Javadoc中有一個小注釋,說明它們是基于值的類 。 其中包括簡短說明的鏈接,以及有關不使用它們的限制。 這很容易被忽略,如果這樣做,則可能會在將來的Java版本中以微妙的方式破壞代碼。 為了避免這種情況,我想在自己的文章中介紹基于價值的類,盡管我已經在其他文章中提到了最重要的部分。
總覽
在詳細說明這些限制之前,本文將首先探討為什么存在基于值的類以及為什么限制了它們的使用(如果您不耐煩,請跳至此處 )。 它將以關于FindBugs的注釋結束,不久便可以為您提供幫助。
背景
讓我們快速了解為什么引入了基于值的類以及JDK中存在的類。
他們為什么存在?
Java的未來版本很可能包含值類型。 我將在未來幾周內寫他們( 所以 留 調整 ),并會在一些細節呈現出來。 盡管它們肯定有好處,但本博文未涉及這些好處,這可能會使限制顯得毫無意義。 相信我,他們不是! 或者不要相信我自己去看 。
現在,讓我們看看我已經寫了一些關于值類型的內容:
該想法的最大簡化是,用戶可以定義一種不同于類和接口的新型類型。 它們的主要特征是它們將不會通過引用(如類)來處理,而是通過值(如基元)來處理。 或者,正如Brian Goetz在他的介紹性文章《價值觀的狀態》中所說的那樣:
像類一樣的代碼,像int一樣工作!
重要的是要添加值類型將是不變的-就像今天的原始類型一樣。
在Java 8中,值類型之前是基于值的類 。 未來它們的精確關系尚不清楚,但可能與裝箱和拆箱原語(例如Integer和int )相似。
設計Optional時,現有類型與將來值類型之間的關系變得很明顯。 在指定和記錄基于價值的類的局限性時也是如此。
存在哪些基于價值的類?
這些都是我在JDK中找到的所有標記為基于值的類:
- java.util: 可選 , OptionalDouble , OptionalLong , OptionalInt
- java.time: 持續時間 , 即時 , LOCALDATE的 , LocalDateTime , 本地時間 , MONTHDAY , OffsetDateTime , OffsetTime , 期間 , 年 , YearMonth , ZonedDateTime , 了zoneid , ZoneOffset
- java.time.chrono: HijrahDate , JapaneseDate , MinguaDate , ThaiBuddhistDate
我無法保證此列表是完整的,因為我沒有找到列出所有列表的官方來源。
發布時間由杰里米·舒爾茨在CC-BY 2.0 。
另外,還有一些非JDK類應該被認為是基于值的,但不要這樣說。 一個例子是Guava的Optional 。 還可以安全地假設大多數代碼庫將包含旨在基于值的類。
有趣的是,現有的拳擊類(如Integer , Double等)未標記為基于值。 這樣做似乎很可取-畢竟它們都是此類的原型-但這樣做會破壞向后兼容性,因為它將使與新限制相抵觸的所有用途追溯無效。
Optional是新的,而免責聲明在第一天就到了。另一方面, Integer可能受到了無可救藥的污染,而且我確信,如果Integer不再是可鎖定的,它將破壞重要代碼的空子(盡管我們可能會這樣認為)練習。)
Brian Goetz – 2015年1月6日(格式化我的)
不過,它們非常相似,因此我們稱它們為“價值至上”。
特點
在這一點上,尚不清楚如何實現值類型,它們的確切屬性是什么以及它們如何與基于值的類交互。 因此,對后者施加的限制不是基于現有要求,而是源自某些所需的值類型特征。 這些限制是否足以在將來與值類型建立關系還不清楚。
話雖如此,讓我們繼續上面的引用:
在Java 8中,值類型之前是基于值的類 。 未來它們的精確關系尚不清楚,但可能與裝箱和拆箱原語(例如Integer和int )相似。 此外,編譯器可能會自由地在兩者之間進行靜默切換以提高性能。 恰恰是,來回切換(即刪除并稍后重新創建引用)也禁止將基于身份的機制應用于基于值的類。
這樣實現的JVM不再需要跟蹤基于值的實例的身份,這可以帶來實質性的性能改進和其他好處。
身分識別
身份一詞在這種情況下很重要,因此讓我們仔細看看。 考慮一個可變對象,該對象會不斷更改其狀態(例如正在修改的列表)。 即使對象總是“看起來”不同,我們仍然會說它是同一對象。 因此,我們區分對象的狀態和身份。 在Java中,狀態相等由equals (如果適當實現)和身份相等通過比較引用來確定。 換句話說,對象的身份由其引用定義。
現在假設JVM將如上所述處理值類型和基于值的類。 在那種情況下,兩者都不會具有有意義的身份。 值類型將沒有一個開始,就像int一樣。 相應的基于值的類僅僅是值類型的盒子,JVM可以隨意銷毀和隨意創建它們。 因此,盡管當然有對單個盒子的引用,但完全不能保證它們將如何存在。
這意味著,即使程序員可以查看代碼并遵循在各處傳遞的基于值的類的實例,JVM的行為也可能有所不同。 它可能會刪除引用(從而破壞對象的標識)并將其作為值類型傳遞。 如果是身份敏感操作,則可能會重新創建一個新引用。
關于身份,最好考慮基于值的類,例如整數:談論“ 3”的不同實例( int )是沒有意義的,談論“ 11:42 pm”的不同實例也沒有意義( LocalTime )。
州
如果基于值的類的實例沒有標識,則只能通過比較它們的狀態(通過實現equals來確定)來確定其equals 。 這具有重要的含義,即狀態相同的兩個實例必須完全可互換,這意味著用另一個實例替換一個這樣的實例必須不會產生任何明顯的影響。
這間接確定了應將哪些內容視為基于值的實例狀態的一部分。 所有類型為基本類型或其他基于值的類的字段都可以成為其一部分,因為它們也可以完全互換(所有“ 3”和“ 11:42 pm”的行為都相同)。 普通班比較棘手。 由于操作可能取決于它們的身份,因此如果基于vale的實例都引用相同但不相同的實例,則通常無法將其交換。
例如,考慮鎖定String ,然后將其包裝在Optional 。 在其他地方,將使用相同的字符序列創建另一個String并將其包裝。 然后,這兩個Optionals不可互換,因為即使它們都包裝了相等的字符序列,這些String實例也不相同,一個實例用作鎖,而另一個實例則充當鎖。
嚴格解釋這意味著基于值的類必須考慮引用本身,而不是將引用字段的狀態包括在其自身的狀態中。 在上面的示例中,僅當Optionals實際指向同一字符串時,才應將其視為相等。
但是,這可能過于嚴格,因為必須對給定的以及其他有問題的示例進行某種程度的解釋。 強制基于值的類忽略諸如String和Integer類的“值-ish”類的狀態非常違反直覺。
值類型框
被計劃為值類型的框會增加一些其他要求。 如果不深入探討值類型,這些將很難解釋,因此我現在不再這樣做。
局限性
首先,需要注意的是,在Java 8中,所有限制都是純人工的。 JVM并不了解這類類的第一件事,并且您可以忽略所有規則而不會出錯。 但這在引入值類型時可能會發生巨大變化。
正如我們在上面看到的,基于值的類的實例沒有保證的身份,在定義相等性方面的寬松程度較低,并且應該符合值類型框的預期要求。 這有兩個含義:
- 該類必須相應地構建。
- 該類的實例不得用于基于身份的操作。
這是Javadoc中所述限制的基礎,因此可以將其分為對類的聲明和其實例的使用的限制。
申報地點
直接來自文檔(編號和格式編號):
基于值的類的實例:
通過上面討論的內容,大多數這些規則都是顯而易見的。
規則1的動機是基于價值的類,即價值類型的框。 出于技術和設計原因,這些必須是最終的且不可更改,并將這些要求轉移到其包裝盒中。
規則2 模糊地解決了有關如何定義基于值的類的狀態的問題。 規則的精確效果取決于對“實例狀態”和“任何其他變量”的解釋。 讀取它的一種方法是在狀態中包括“值-ish”類,并將典型的引用類型視為其他變量。
第3到第6個數字考慮丟失的身份。
有趣的是, Optional打破了規則2,因為它在包裝的值上調用了equals 。 同樣, java.time和java.time.chrono所有基于值的類都通過可序列化(這是一個基于身份的操作,請參見下文) java.time.chrono打破規則3。
使用網站
再次從文檔中:
如果程序嘗試直接通過引用相等性或通過呼吁同步,身份哈希,序列化或任何其他身份敏感機制間接地將兩個引用區分為基于值的類的相等值,則可能會產生不可預測的結果。
考慮到丟失的身份,直接區分參考是不言而喻的。 但是,沒有任何解釋說明為什么列出的示例違反了該規則,所以讓我們仔細看看。 我列出了所有可以解決的違規事項,并給出了簡短的解釋和具體案例( vbi代表基于值的類的實例 ):
參考比較:這顯然根據實例的身份來區分實例。
vbi的序列化:希望使值類型可序列化,并且有意義的定義似乎很簡單。 但是,今天,序列化對對象身份做出了承諾,這與基于身份的無價值類的概念相沖突。 在當前的實現中,序列化在遍歷對象圖時也使用對象標識。 因此,目前,必須將其視為基于身份的操作,應避免使用。
情況:
- 可序列化類中的非臨時字段
- 通過ObjectOutputStream.writeObject直接序列化
鎖定vbi:使用對象標頭訪問實例的監視器–基于值的類的標頭可以自由刪除和重新創建,并且基本/值類型沒有標頭。
情況:
- 在同步塊中使用
- 調用Object.wait,Object.notify或Object.notifyAll
身份哈希碼:要求該哈希碼在實例的生存期內保持不變。 由于基于價值的類的實例可以自由刪除并重新創建,因此對于開發人員來說有意義的意義不能得到保證。
情況:
- System.identityHashCode的參數
- 鍵入IdentityHashMap
突出強調其他違規或改進說明的評論,深表感謝!
查找錯誤
當然,了解所有這一切是很好的,但這并不意味著阻止您超越規則的工具并不會真正有幫助。 作為FindBugs的重度用戶,我決定要求項目實施此功能,并創建了功能請求 。 該票證涵蓋了使用場所的限制,并將幫助您在JDK以及您自己的基于值的類(帶有注釋的類)中維護它們。
出于對FindBugs的好奇并希望做出貢獻,我決定著手嘗試自己實施它。 因此,如果您要問為什么花這么長時間準備好該功能,現在您知道了:這是我的錯。 但是談話很便宜,所以為什么不加入我的行列呢? 我在GitHub上放置了一個FindBugs克隆 ,您可以看到此pull請求中的進度。
一旦完成,我計劃也要實現聲明站點的規則,因此可以確保在值類型最終出現時,正確編寫了基于值的類并準備就緒。
反射
我們已經看到,基于值的類是值類型的先驅。 隨著Java的變化,這些實例將沒有有意義的身份,并且定義它們的狀態的可能性也將受到限制,這將對其聲明和使用產生限制。 這些限制已詳細討論。
翻譯自: https://www.javacodegeeks.com/2015/02/value-based-classes.html
總結
- 上一篇: 一类医疗器械生产备案流程代办(一类医疗器
- 下一篇: 安卓手机当麦克风给电脑用(安卓手机当麦克