IFreezable 介面
定義
重要
部分資訊涉及發行前產品,在發行之前可能會有大幅修改。 Microsoft 對此處提供的資訊,不做任何明確或隱含的瑕疵擔保。
提供靈活的存取控制機制,且不要求類別必須是不變的。
[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<A> {
...
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("Attempt to modify frozen object");
}
<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 & modify
void setStuff(Collection x) {
stuff = x;
} // caller could keep reference & modify
MyClass(Collection x) {
stuff = x;
} // caller could keep reference & 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 |
回傳包裹實例的 |
| JniManagedPeerState |
管理貴族的狀況。 (繼承來源 IJavaPeerable) |
| JniObjectReferenceControlBlock |
提供靈活的存取控制機制,且不要求類別必須是不變的。 (繼承來源 IJavaPeerable) |
| JniPeerMembers |
成員存取與召喚支援。 (繼承來源 IJavaPeerable) |
| PeerReference |
回傳JniObjectReference包裹後的 Java 物件實例。 (繼承來源 IJavaPeerable) |
方法
| 名稱 | Description |
|---|---|
| CloneAsThawed() |
提供複製人行動的規定。 |
| Disposed() |
當實例被處理後才被召喚。 (繼承來源 IJavaPeerable) |
| DisposeUnlessReferenced() |
如果沒有未解決的參考資料,則 |
| Finalized() |
當實例完成後才會被通知。 (繼承來源 IJavaPeerable) |
| Freeze() |
凍結物體。 |
| SetJniIdentityHashCode(Int32) |
將回傳的值設為 |
| SetJniManagedPeerState(JniManagedPeerStates) |
提供靈活的存取控制機制,且不要求類別必須是不變的。 (繼承來源 IJavaPeerable) |
| SetPeerReference(JniObjectReference) |
將回傳的值設為 |
| UnregisterFromRuntime() |
取消註冊此實例,讓執行時不會在未來 Java.Interop.JniRuntime+JniValueManager.PeekValue 的呼叫中回傳該實例。 (繼承來源 IJavaPeerable) |
擴充方法
| 名稱 | Description |
|---|---|
| GetJniTypeName(IJavaPeerable) |
取得實例 |
| JavaAs<TResult>(IJavaPeerable) |
試著強制 |
| JavaCast<TResult>(IJavaObject) |
執行 Android 執行時間檢查的類型轉換。 |
| JavaCast<TResult>(IJavaObject) |
提供靈活的存取控制機制,且不要求類別必須是不變的。 |
| TryJavaCast<TResult>(IJavaPeerable, TResult) |
試著強制 |