語言

IFreezable 介面

定義

提供靈活的存取控制機制,且不要求類別必須是不變的。

[Android.Runtime.Register("android/icu/util/Freezable", "", "Android.Icu.Util.IFreezableInvoker", ApiSince=24)]
[Java.Interop.JavaTypeParameters(new System.String[] { "T" })]
public interface IFreezable : IDisposable, Java.Interop.IJavaPeerable, Java.Lang.ICloneable
[<Android.Runtime.Register("android/icu/util/Freezable", "", "Android.Icu.Util.IFreezableInvoker", ApiSince=24)>]
[<Java.Interop.JavaTypeParameters(new System.String[] { "T" })>]
type IFreezable = interface
    interface ICloneable
    interface IJavaObject
    interface IDisposable
    interface IJavaPeerable
衍生
屬性
實作

備註

提供靈活的存取控制機制,且不要求類別必須是不變的。 一旦凍結,物件就永遠無法再被解凍,因此從那時起它就無法執行緒安全。 一旦物件被凍結,必須保證無法再對其進行任何更改。 任何更改該規則的嘗試都必須觸發 UnsupportedOperationException 例外。 這表示當物件回傳內部物件,或有人有這些內部物件的參考時,這些內部物件必須是不可變的,或若嘗試修改它們,也必須觸發例外。 當然,物件可以回傳內部物件的複製,因為那些是安全的。 <h2>背景</h2>

有時候你需要物件是「安全」的,這樣才不會被修改。 例如當物件需要執行緒安全、撰寫穩健程式碼時,或是在快取中。 如果你只是自己創造物件,當然可以保證這點——但前提是你不會犯錯。 如果你是被人直接交付的物品,或是用他人被交付來創造物品,那情況就不一樣了。 這一切都取決於你是否想採取布蘭奇·杜波依斯(Blanche Dubois)那種方法(”取決於陌生人的善意。)或是Andy Grove的方法(”只有偏執者能存活下來。

例如,假設我們有一個簡單的類別:

public class A {
                 protected Collection b;

                 protected Collection c;

                 public Collection get_b() {
                         return b;
                 }

                 public Collection get_c() {
                         return c;
                 }

                 public A(Collection new_b, Collection new_c) {
                         b = new_b;
                         c = new_c;
                 }
            }

因為這個職業沒有二傳手,有人可能會認為它是不可變的。 你當然知道這會帶你去哪裡;這門課在多方面都不安全。 以下說明了這一點。

public test1(SupposedlyImmutableClass x, SafeStorage y) {
               // unsafe getter
               A a = x.getA();
               Collection col = a.get_b();
               col.add(something); // a has now been changed, and x too

               // unsafe constructor
               a = new A(col, col);
               y.store(a);
               col.add(something); // a has now been changed, and y too
             }

有幾種不同的技巧可以讓課程安全。

<奧爾><利>康斯特反對。 在 C++ 中,你可以宣告參數 const。</li><li>不變的包裝紙。 舉例來說,你可以把一個集合放在不可變封裝中。</李><麗>Always-Immutable 物件。 Java 採用此方法,但有一些變化。 範例: <ol><li>Simple。 一旦顏色被建立(例如由 R、G 和 B 整數組成),它便是不可變的。</li><li>建築班。 還有一個獨立的「建造者」類別。 例如,可修改字串是使用 StringBuffer 建立的(該軟體沒有完整的字串 API)。 一旦你想要一個不變的表單,就用 toString()< 建立一個。/li><li>原始人。 這些方法總是安全的,因為它們會從方法複製到輸入/輸出時。</li></ol></li><li 克>隆。 當你需要一個物件以確保安全時,就複製它。</li></ol>

這些各有優缺點。

<ol><li>Const 提供一定程度的保護,但由於 const 可能且常被拋棄,它只能防止大多數無意的錯誤。 它也沒有執行緒保護,因為任何在其他執行緒中指向(未整合)物件的指標的人都可能搞砸你。</li><li>不變包裝比 const 更安全,因為 constness 無法被丟棄。 但除此之外,它們有相同的問題:如果有人持有原始物件,或類別回傳的任何物件是可變的,這些物件就不安全。</li><li>Always-Immutable 物件是安全的,但使用時可能需要過度建立物件。</li><li>克隆只有在物件真正擁有「安全」克隆時才安全;定義為確保克隆的任何變更都不會影響原始克隆。 不幸的是,許多物件沒有「安全」的複製,且不斷複製可能需要大量建立物件。</li></ol><h2>冷凍模型</h2>

模型 Freezable 補充這些選擇,讓你能透過呼叫各種方法來建立物件,當物件進入最終狀態時,你可以 讓它變得 不可變。 一旦物件不可變, 就永遠無法 被修改,且完全執行緒安全:也就是說,多個執行緒可以互相引用,且不需同步。 如果有人需要一個可變的物件版本,他們可以使用 cloneAsThawed()並修改該複製品。 這在替代方案不足或笨拙的情況下,提供了一個簡單且有效的安全類別機制。 (如果一個物件在變為不變之前就被共享,則每個執行緒都有責任將其使用方式互斥(mutex),就像其他物件一樣。)

根據物件類型,實作此介面需要做以下步驟。

<h3><b>不可變物件</b></h3>

這些是最簡單的。 你只要用介面來反映這點,請加上以下內容:

public class A implements Freezable&lt;A&gt; {
              ...
              public final boolean isFrozen() {return true;}
              public final A freeze() {return this;}
              public final A cloneAsThawed() { return this; }
              }

這些可以是最終方法,因為不可變物件的子類本身必須是不變的。 (註: freeze 回來 this 進行連鎖。)

<h3><b>可變物件</b></h3>

新增一個受保護的「標記」欄位:

protected volatile boolean frozen; // WARNING: must be volatile

新增以下方法:

public final boolean isFrozen() {
                 return frozen;
            };

            public A freeze() {
                 frozen = true;  // WARNING: must be final statement before return
                 return this;
            }

新增一個 cloneAsThawed() 遵循正常模式的方法, clone()但新複製品中不 frozen=false 符。

接著取設定器(即任何能改變物件內部狀態的方法),並加入以下作為第一句陳述:

if (isFrozen()) {
                 throw new UnsupportedOperationException(&quot;Attempt to modify frozen object&quot;);
            }

<h4><b>子職業</b></h4>

任何子 Freezable 職業都會使用其超職業的標記欄位。 它必須覆寫 freeze() 並 cloneAsThawed() 呼叫超類別,但通常不會覆寫 isFrozen()。 接著它必須專注於自己的接球手、二傳手和場上球員。

<h4><b>內部快取</b></h4>

內部快取是指物件在邏輯上未被修改,但物件內部狀態會改變的情況。 例如,有些 cont C++ 函式會丟棄 “ 上的 cont;這個”指標以修改物件快取。 這些情況會透過內部快取的互斥來處理,以確保執行緒安全。 例如,假設 UnicodeSet 有一個內部標記,指向最後被存取的碼點。 在這種情況下,欄位外部看不到,所以你只需要同步欄位以保護執行緒安全。

<h4>不安全內部存取</h4>

若內部欄位是不可變的或不可變的(如字串或原語),則稱為frozen欄位。 如果你從未允許內部存取這些資料,那你就完了。 例如,將 UnicodeSet 轉換成 就是 Freezable 透過上述步驟完成。 但請記得,如果你在 getter、setter 或 constructor 裡有任何類似的程式碼,你 b<已經>/b< 允許存取不安全的內部元件:>

Collection getStuff() {
                 return stuff;
            } // caller could keep reference &amp; modify

            void setStuff(Collection x) {
                 stuff = x;
            } // caller could keep reference &amp; modify

            MyClass(Collection x) {
                 stuff = x;
            } // caller could keep reference &amp; modify

這些也在上方 b<Background>/b< 的程式碼範例>中有所說明。

處理不安全的內部設備,最簡單的做法是親自處理 freeze() 工作。 只要把所有內部欄位凍結,然後設定凍結旗標即可。 之後的接球手/二傳手都能正常運作。 舉個例子:

<b>警告!</b> 「凍結」的布林值必須是揮發性的,且必須設為方法的最後一個語句。

public A freeze() {
                 if (!frozen) {
                         foo.freeze();
                         frozen = true;
                 }
                 return this;
            }

如果欄位是 Collection 或 Map,那麼要讓它凍結,你有兩個選擇。 如果你從未允許從物件外部存取集合,那就包裝它以防止未來修改。

zone_to_country = Collections.unmodifiableMap(zone_to_country);

如果你 曾經 允許進入,請在包裹前先做檢查 clone() 。

zone_to_country = Collections.unmodifiableMap(zone_to_country.clone());

如果一個集合 (或其他物件容器) 本身可以包含可變物件,那麼為了建立安全的複製,你需要遞迴通過它,讓整個集合變得不可變。 遞迴程式碼應選擇最具體的集合,以避免日後需要降殼。

<引用>

<b>注意:</b>Java 一個惱人的缺陷是,像 或 Map這樣的通用集合Set沒有操作clone()。 當你不知道收藏的類型時,最簡單的方法就是建立一個新的收藏:

zone_to_country = Collections.unmodifiableMap(new HashMap(zone_to_country));

</區塊引用>

的 android.icu.util.FreezableJava 檔。

此頁面的部分是根據 Android 開放原始碼專案所建立和共用的工作進行修改,並根據 Creative Commons 2.5 屬性授權中所述的詞彙使用。

屬性

名稱 Description
Handle

取得底層 Android 物件的 JNI 值。

(繼承來源 IJavaObject)
IsFrozen

判斷物體是否被凍結。

JniIdentityHashCode

回傳包裹實例的 java.lang.System.identityHashCode() 值。

(繼承來源 IJavaPeerable)
JniManagedPeerState

管理貴族的狀況。

(繼承來源 IJavaPeerable)
JniObjectReferenceControlBlock

提供靈活的存取控制機制,且不要求類別必須是不變的。

(繼承來源 IJavaPeerable)
JniPeerMembers

成員存取與召喚支援。

(繼承來源 IJavaPeerable)
PeerReference

回傳JniObjectReference包裹後的 Java 物件實例。

(繼承來源 IJavaPeerable)

方法

名稱 Description
CloneAsThawed()

提供複製人行動的規定。

Disposed()

當實例被處理後才被召喚。

(繼承來源 IJavaPeerable)
DisposeUnlessReferenced()

如果沒有未解決的參考資料,則 Dispose()呼叫;否則,則不做任何事。

(繼承來源 IJavaPeerable)
Finalized()

當實例完成後才會被通知。

(繼承來源 IJavaPeerable)
Freeze()

凍結物體。

SetJniIdentityHashCode(Int32)

將回傳的值設為 JniIdentityHashCode。

(繼承來源 IJavaPeerable)
SetJniManagedPeerState(JniManagedPeerStates)

提供靈活的存取控制機制,且不要求類別必須是不變的。

(繼承來源 IJavaPeerable)
SetPeerReference(JniObjectReference)

將回傳的值設為 PeerReference。

(繼承來源 IJavaPeerable)
UnregisterFromRuntime()

取消註冊此實例,讓執行時不會在未來 Java.Interop.JniRuntime+JniValueManager.PeekValue 的呼叫中回傳該實例。

(繼承來源 IJavaPeerable)

擴充方法

名稱 Description
GetJniTypeName(IJavaPeerable)

取得實例 self類型的 JNI 名稱。

JavaAs<TResult>(IJavaPeerable)

試著強制self輸入 TResult,檢查 強制在 Java 端是否有效。

JavaCast<TResult>(IJavaObject)

執行 Android 執行時間檢查的類型轉換。

JavaCast<TResult>(IJavaObject)

提供靈活的存取控制機制,且不要求類別必須是不變的。

TryJavaCast<TResult>(IJavaPeerable, TResult)

試著強制self輸入 TResult,檢查 強制在 Java 端是否有效。

適用於