介紹
不可變對象具備執行緒安全的特性。此外,相較於可變對象,不可變對象通常也較合理,易於了解,而且提供較高的安全性。
小型的不可變對象可以被有效率的複製,但是較大的不可變對象,如果想要有效率的被複製,就需要更複雜的一致性數據結構(persistent data structure)算法。因為效能的緣故,有時候會以可變對象來加以取代不可變對象。
概念
不可變的變數
在命令式編程中,在內容永不改變的程式變數中保存的值被稱為常量,以將它們與可在執行期間改變的變數區分開來。示例包括從米到英尺的轉換因子,或pi到幾個小數位的轉換因子。
程式運行時可以計算唯讀欄位(不同於事先已知的常量),但在初始化之後永遠不會更改。
弱勢與強大的不變性
有時,人們會談論某個對象的某些欄位是不可變的。這意味著無法更改對象狀態的那些部分,即使對象的其他部分可能是可更改的(弱不可變)。如果所有欄位都是不可變的,則該對象是不可變的。如果整個對象不能被另一個類擴展,則該對象被稱為強不可變的。[3]例如,這可能有助於明確強制某些不變數,這些不變數關於對象中的某些數據在對象的生命周期內保持不變。在某些語言中,這是通過一個關鍵字(例如C ++中的const,Java中的final)來完成的,該關鍵字將欄位指定為不可變的。在某些語言中,它是相反的:在OCaml中,對象或記錄的欄位默認是不可變的,需要用mutable明確標記為。
對象的引用
在大多數面向對象的語言中,可以使用引用引用對象。這些語言的一些示例是Java,C ++,C#,VB.NET和許多腳本語言,例如Perl,Python和Ruby。在這種情況下,當通過引用共享對象時,對象的狀態是否可以變化很重要。
複製對象
如果已知某個對象是不可變的,則可以通過複製對它的引用而不是複製整個對象來複製它。由於引用(通常只是指針的大小)通常比對象本身小得多,因此可以節省記憶體並提高執行速度。
參考複製技術對於可變對象使用起來要困難得多,因為如果引用可變對象的任何用戶更改它,則該引用的所有其他用戶都將看到該更改。如果這不是預期的效果,則可能難以通知其他用戶讓他們正確回響。在這些情況下,整個對象而不是參考的防禦性複製通常是一種簡單但成本高昂的解決方案。觀察者模式是處理可變對象變化的替代技術。
寫入時複製
一種融合了可變對象和不可變對象的優點並且幾乎在所有現代硬體中都直接支持的技術是寫時複製(COW)。使用此技術,當用戶要求系統複製對象時,它將僅創建仍指向同一對象的新引用。一旦用戶嘗試通過特定引用修改對象,系統就會生成一個真實副本,對其套用修改,並設定引用以引用新副本。其他用戶不受影響,因為他們仍然引用原始對象。因此,在COW下,所有用戶似乎都具有其對象的可變版本,但是在用戶不修改其對象的情況下,保留了不可變對象的節省空間和速度優勢。寫時複製在虛擬記憶體系統中很流行,因為它允許它們在正確處理應用程式可能執行的任何操作的同時節省記憶體空間。
實習
始終使用引用代替相等對象的副本的做法稱為實習。如果使用interning,則若且唯若它們的引用(通常表示為指針或整數)相等時,才認為兩個對象相等。有些語言會自動執行此操作:例如,Python會自動插入短字元串。如果在每種情況下都可以保證實現實習的算法,那么比較對象的相等性就會減少,以便比較它們的指針 - 大多數應用程式的速度都會大幅增加。 (即使算法不能保證是全面的,當對象相等並使用相同的引用時,仍然存在快速路徑案例改進的可能性。)實習通常僅對不可變對象有用。
執行緒安全
不可變對象在多執行緒應用程式中很有用。多個執行緒可以對不可變對象表示的數據起作用,而不用擔心其他執行緒正在更改數據。因此,不可變對象被認為比可變對象更具執行緒安全性。
語言特定的細節
在Python,Java和.NET Framework中,字元串是不可變對象。 Java和.NET Framework都有可變的字元串版本。 在Java中,這些是StringBuffer和StringBuilder(Java String的可變版本),在.NET中,這是StringBuilder (.Net String的可變版本)。 Python 3有一個可變的字元串(位元組)變體,名為bytearray。
此外,Java中的所有原始包裝類都是不可變的。類似的模式是不可變接口和不可變包裝器。
在純函式式程式語言中,不可能在不擴展語言的情況下創建可變對象(例如,通過可變引用庫或外部函式接口),因此所有對象都是不可變的。
Ada
在Ada中,任何對象都通過constant關鍵字聲明為變數(即可變;通常是隱式默認值)或常量(即不可變)。
子程式參數在in模式下是不可變的,在in out和out模式下是可變的。
C++
在C ++中,Cart的const-correct實現允許用戶通過提供兩個不同版本的getItems()方法,根據需要將類的新實例聲明為const(不可變)或可變 。 (請注意,在C ++中,沒有必要 - 實際上不可能 - 為const實例提供專門的構造函式。)
C#
在C#中,您可以使用readonly語句強制實現類的欄位的不變性。通過強制所有欄位不可變,您將獲得不可變類型。