日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

不变性的来龙去脉

發(fā)布時(shí)間:2023/12/3 编程问答 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 不变性的来龙去脉 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

因此,在我的第一篇文章中,我談到了一些構(gòu)建器模式,并提到了一個(gè)非常強(qiáng)大但卻被忽視的概念:不變性。

什么是不可變類? 這只是一個(gè)其實(shí)例無法修改的類。 類屬性的每個(gè)值都在其聲明或其構(gòu)造函數(shù)中設(shè)置,并在對象的整個(gè)生命周期中保留這些值。 Java有很多不變的類,例如String ,所有帶框的原語( Double , Integer , Float等), BigInteger和BigDecimal等。 這有一個(gè)很好的理由:不可變類比可變類更容易設(shè)計(jì),實(shí)現(xiàn)和使用。 一旦實(shí)例化它們,它們只能處于一種狀態(tài),因此它們不易出錯(cuò),并且,正如我們在本文后面會看到的那樣,它們更加安全。

您如何確保類是不變的? 只需遵循以下5個(gè)簡單步驟:

  • 不要提供任何可修改對象狀態(tài)的公共方法 ,也稱為增變器(例如setter)。
  • 防止擴(kuò)展類 。 這不允許任何惡意或粗心的類擴(kuò)展我們的類并損害其不變的行為。 這樣做的通常且更簡單的方法是將類標(biāo)記為final ,但是我將在本文中提及另一種方法。
  • 將所有字段定為最終值 。 這是讓編譯器為您強(qiáng)制執(zhí)行第1點(diǎn)的方法。 此外,它清楚地使任何看到您的代碼的人都知道,您不希望這些字段在設(shè)置后更改其值。
  • 將所有字段設(shè)為私有 。 無論您是否考慮不變性,這一點(diǎn)都應(yīng)該很明顯,并且您應(yīng)該遵循它 ,但是我只是為了以防萬一。
  • 永遠(yuǎn)不要提供對任何可變屬性的訪問 。 如果您的類具有一個(gè)可變對象作為其屬性之一(例如List , Map或您的域問題中的任何其他可變對象),請確保該類的客戶端永遠(yuǎn)無法獲得對該對象的引用。 這意味著您永遠(yuǎn)不要從訪問器(例如,getter)直接返回對它們的引用,并且永遠(yuǎn)不要在構(gòu)造函數(shù)中使用從客戶端作為參數(shù)傳遞的引用來初始化它們。 在這種情況下,您應(yīng)該始終制作防御性副本。
  • 有很多理論,沒有代碼,因此讓我們看一個(gè)簡單的不可變類是什么樣子,以及它如何處理我之前提到的5個(gè)步驟:

    public class Book {private final String isbn;private final int publicationYear;private final List reviews;private Book(BookBuilder builder) {this.isbn = builder.isbn;this.publicationYear = builder.publicationYear;this.reviews = Lists.newArrayList(builder.reviews);}public String getIsbn() {return isbn;}public int getPublicationYear() {return publicationYear;}public List getReviews() {return Lists.newArrayList(reviews);}public static class BookBuilder {private String isbn;private int publicationYear;private List reviews;public BookBuilder isbn(String isbn) {this.isbn = isbn;return this;}public BookBuilder publicationYear(int year) {this.publicationYear = year;return this;}public BookBuilder reviews(List reviews) {this.reviews = reviews == null ? new ArrayList() : reviews;return this;}public Book build() {return new Book(this);}} }

    我們將在這個(gè)非常簡單的課程中講解重點(diǎn)。 首先,您可能已經(jīng)注意到,我再次使用了構(gòu)建器模式。 這不僅是因?yàn)槲沂撬闹覍?shí)擁護(hù)者,而且還因?yàn)槲蚁胝f明一些我不想在之前的帖子中沒有首先對不變性概念有基本了解的觀點(diǎn)。 現(xiàn)在,讓我們看一下我提到的5個(gè)步驟,您需要遵循這些步驟使一個(gè)類不可變,并查看它們是否對本書示例有效:

      • 不要提供任何修改對象狀態(tài)的公共方法 。 請注意,該類上的唯一方法是其私有構(gòu)造函數(shù)和其屬性的獲取器,但沒有更改對象狀態(tài)的方法。
      • 防止擴(kuò)展類 。 這個(gè)很棘手。 我提到確保這一點(diǎn)的最簡單方法是將班級定為最終班,但Book班顯然不是最終班。 但是,請注意,唯一可用的構(gòu)造函數(shù)是private 。 編譯器確保沒有公共或受保護(hù)的構(gòu)造函數(shù)的類不能被子類化。 因此,在這種情況下,不必在類聲明中使用final關(guān)鍵字,但無論如何將其包括在內(nèi)只是一個(gè)好主意,以使看到您代碼的任何人都可以清楚地知道。
      • 將所有字段定為最終值 。 非常簡單,該類上的所有屬性都聲明為final 。
      • 永遠(yuǎn)不要提供對任何可變屬性的訪問 。 這實(shí)際上很有趣。 注意Book類如何具有一個(gè)List <String>屬性,該屬性被聲明為final,并且其值在類構(gòu)造函數(shù)上設(shè)置。 但是,此列表是可變對象。 也就是說,雖然評論參考一旦設(shè)置就無法更改,但列表的內(nèi)容可以更改。 引用相同列表的客戶端可以添加或刪除元素,結(jié)果,在創(chuàng)建Book對象后更改其狀態(tài)。 因此,請注意,在Book構(gòu)造函數(shù)中,我們不直接分配引用。 相反,我們使用Guava庫通過調(diào)用“ this.reviews = Lists.newArrayList(builder.reviews); ”來復(fù)制列表this.reviews = Lists.newArrayList(builder.reviews); ”。 在getReviews方法上可以看到相同的情況,在該方法中,我們返回列表的副本而不是直接引用。 值得注意的是,此示例可能有點(diǎn)過于簡化,因?yàn)樵u論列表只能包含不可變的字符串。 如果列表的類型是可變的類,那么您還必須復(fù)制列表中的每個(gè)對象,而不僅僅是列表本身。

    最后一點(diǎn)說明了為什么不可變的類導(dǎo)致更簡潔的設(shè)計(jì)和更易于閱讀的代碼。 您只需共享那些不可變的對象,而不必?fù)?dān)心防御性副本。 實(shí)際上,絕對不要制作任何副本,因?yàn)閷ο蟮娜魏胃北径紝⒂肋h(yuǎn)等于原始副本。 一個(gè)必然的結(jié)論是,不變的對象僅僅是簡單的。 他們只能處于一種狀態(tài),并且一生都保持這種狀態(tài)。 您可以使用類構(gòu)造函數(shù)來檢查任何不變量(即,需要在該類上有效的條件,例如其屬性之一的值范圍),然后可以確保這些不變量保持真實(shí)狀態(tài)而無需付出任何努力您或您的客戶。

    不變對象的另一個(gè)巨大好處是它們本質(zhì)上是線程安全的。 它們不能被同時(shí)訪問對象的多個(gè)線程破壞。 到目前為止,這是在應(yīng)用程序中提供線程安全性的最簡單且不易出錯(cuò)的方法。

    但是,如果您已經(jīng)有一個(gè)Book實(shí)例并且想要更改其屬性之一的值怎么辦? 換句話說,您想要更改對象的狀態(tài)。 在不可變的類上,根據(jù)定義,這是不可能的。 但是,就像軟件中的大多數(shù)事情一樣,總有一種解決方法。 在這種情況下,實(shí)際上有兩個(gè)。

    第一種選擇是在Book類上使用Fluent Interface技術(shù),并具有類似于setter的方法,這些方法實(shí)際上創(chuàng)建一個(gè)對象,該對象的所有屬性都具有相同的值,但要更改的對象除外。 在我們的示例中,我們將必須在Book類中添加以下內(nèi)容:

    private Book(BookBuilder builder) {this(builder.isbn, builder.publicationYear, builder.reviews);}private Book(String isbn, int publicationYear, List reviews) {this.isbn = isbn;this.publicationYear = publicationYear;this.reviews = Lists.newArrayList(reviews);}public Book withIsbn(String isbn) {return new Book(isbn,this.publicationYear, this.reviews);}

    請注意,我們添加了一個(gè)新的私有構(gòu)造函數(shù),可以在其中指定每個(gè)屬性的值,并修改舊的構(gòu)造函數(shù)以使用新的構(gòu)造函數(shù)。 另外,我們添加了一個(gè)新方法,該方法返回一個(gè)新的Book對象,該對象具有我們想要的isbn屬性值。 相同的概念適用于該類的其余屬性。 之所以稱為功能方法,是因?yàn)榉椒o需修改即可返回對其參數(shù)進(jìn)行操作的結(jié)果。 這與程序或命令式方法形成對比,在方法式或命令式方法中,方法將一個(gè)過程應(yīng)用于其操作數(shù),從而更改其狀態(tài)。

    這種生成新對象的方法顯示了不可變類的唯一真正缺點(diǎn):它們要求我們?yōu)樗璧拿總€(gè)不同值創(chuàng)建一個(gè)新對象,這會在性能和內(nèi)存消耗方面產(chǎn)生可觀的開銷。 如果要更改對象的幾個(gè)屬性,則會放大此問題,因?yàn)樵诿總€(gè)步驟中都將生成一個(gè)新對象,并且最終將丟棄所有中間對象并僅保留最后一個(gè)結(jié)果。

    我們可以在構(gòu)建器模式的幫助下為多步操作提供更好的選擇,例如我在上一段中描述的操作。 基本上,我們向構(gòu)建器添加一個(gè)新的構(gòu)造函數(shù),該構(gòu)造函數(shù)采用一個(gè)已經(jīng)創(chuàng)建的實(shí)例來設(shè)置其所有初始值。 然后,客戶端可以以通常的方式使用構(gòu)建器來設(shè)置所有所需的值,然后使用build方法來創(chuàng)建最終對象。 這樣,我們避免只使用我們需要的某些值來創(chuàng)建中間對象。 在我們的示例中,此技術(shù)在生成器方面看起來像這樣:

    public BookBuilder(Book book) {this.isbn = book.getIsbn();this.publicationYear = book.getPublicationYear();this.reviews = book.getReviews(); }

    然后,在我們的客戶上,我們可以:

    Book originalBook = getRandomBook();Book modifiedBook = new BookBuilder(originalBook).isbn('123456').publicationYear(2011).build();

    現(xiàn)在,顯然該構(gòu)建器不是線程安全的,因此您必須采取所有常用的預(yù)防措施,例如不與多個(gè)線程共享一個(gè)構(gòu)建器。

    我提到過這樣一個(gè)事實(shí),即我們必須為狀態(tài)的每個(gè)變化都創(chuàng)建一個(gè)新對象,這可能會增加性能,這是不可變類的唯一真正的缺點(diǎn)。 但是,對象創(chuàng)建是JVM不斷改進(jìn)的方面之一。 實(shí)際上,除特殊情況外,對象創(chuàng)建比您想象的要高效得多。 無論如何,提出一個(gè)簡單明了的設(shè)計(jì)通常是一個(gè)好主意,然后僅在進(jìn)行測量后才重構(gòu)性能。 當(dāng)您嘗試猜測代碼在何處花費(fèi)大量時(shí)間時(shí),十分之九會發(fā)現(xiàn)您錯(cuò)了。 此外,不變對象可以自由共享而不必?fù)?dān)心后果,這一事實(shí)使您有機(jī)會鼓勵客戶端盡可能重用現(xiàn)有實(shí)例,從而大大減少了創(chuàng)建對象的數(shù)量。 一種常見的方法是為最常見的值提供公共靜態(tài)最終常量。 此技術(shù)在JDK上大量使用,例如在Boolean.FALSE或BigDecimal.ZERO中 。

    總結(jié)一下這篇文章,如果您想從中學(xué)到一些東西,那就這樣吧: 除非有充分的理由使類可變,否則類應(yīng)該是不可變的 。 不要為每個(gè)類屬性自動添加設(shè)置器。 如果由于某種原因您絕對不能使您的類不可變,那么請盡可能限制其可變性。 一個(gè)對象可以處于的狀態(tài)越少,就越容易考慮該對象及其不變量。 而且不必?fù)?dān)心不變性的性能開銷,很有可能您不必?fù)?dān)心它。

    參考: JCG合作伙伴 Jose Luis在開發(fā)上的 不變性的來龍去脈 。

    翻譯自: https://www.javacodegeeks.com/2013/01/the-ins-and-outs-of-immutability.html

    總結(jié)

    以上是生活随笔為你收集整理的不变性的来龙去脉的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。