2012-04-27 98 views
2

我想在java中創建可變和不可變節點,兩者在除mutable之外的所有內容中都應該相同。如何實現基類和兩個派生類的可變類和不可變類?可變和不可變類

+0

這是什麼樣的「節點」?什麼是上下文?這些課程是否應該繼承? – 2012-04-27 22:05:42

回答

4

可變類和不可變類之間的區別在於不可變類沒有setter或其他修改內部狀態的方法。狀態只能在構造函數中設置。

調用父類不可變將是一個壞主意,因爲當你有子類時,這將不再是真的。這個名字會讓人誤解:

ImmutableNode node = new MutableNode(); 
((MutableNode)node).change(); 
+0

即使沒有setter類也可以使用反射進行修改。只有最後的變量保證不變性 – birdy 2012-04-27 15:01:11

+0

@myx:我同意字段*應該*在最終的不可變類中標記(但是出於不同於您建議的原因)。然而,這在這裏不起作用,因爲那時他的子類化想法是不可能的。可變類也不能修改最終字段。儘管說實話,我不喜歡首先使用繼承來實現可變性的想法。還有其他方法可以不使用繼承來共享代碼:例如界面和組成。 – 2012-04-27 15:03:23

+2

@myx --- bzzztt。您可以通過反射來更改最終變量...除了可以編譯時間常量表達式的靜態最終字段。然而,通過反思改變私人和/或最終變量被認爲是壞事......並且只能作爲絕望的最後措施來完成。 – 2012-04-27 15:03:29

1

不可變的類是一個曾經創建過的類,它的內容不能被改變。不可變的對象是其狀態不能被改變的對象。

Java中不可變類的常見示例是String類。

4

所有你需要做的是建立一個單一的基類受保護的變量

public class Base{ 
    protected int foo; 
} 

易變的人需要能夠設置變量

public class MutableBase extends Base{ 
    public void setFoo(){} 
} 

的immmutable一個需要能夠只設置一次變量

public class ImmutableBase extends Base{ 
    public ImmutableBase(int foo){ 
      this.foo = foo; 
    } 
} 

大多數不可變的類,有​​方法來操作t他內部變量沒有變異的實例。字符串這樣做,你可能會想這樣的事情

public ImmutableBase add(int bar){ 
    return new ImmutableBase(this.foo+bar); 
} 

關於這個很酷的事情是,你給你的類的用戶控制/憂每個實例的內部。這使得它更容易處理,因爲在Java中,所有東西都是通過對象引用傳遞的,所以如果你傳遞一個String或一個ImmutableBase,你不必擔心它被改變了。

+0

編譯錯誤。如果你這樣做,'foo'不能是私人的。 – 2012-04-27 15:01:49

+0

@StephenC - 哎呀,謝謝 – dfb 2012-04-27 15:02:51

+0

怎麼樣宣佈領域它自己作爲最終? – nabil 2012-04-27 17:14:11

1

對於一個類是不可變的,它必須被聲明爲final,並且它不得使用方法。最終的聲明確保它不能被擴展,並添加額外的可變屬性。

class Base { 
    protected int var1; 
    protected int var2; 

    public getVar1() {return var1;} 
    public getVar2() {return var2;} 
    } 

    class Mutable extends Base { 
     public setVar1(int var1) {this.var1 = var1} 
     public setVar2(int var2) {this.var2 = var2} 
    } 

    final class Immutable extends Base { //final to avoid being extended and then implement the setters 

    } 

那就是我可以做的小事?但爲什麼你需要這樣一個場景?

+0

那麼聲明它自己作爲最終的字段呢? – nabil 2012-04-27 17:13:41

+0

,因爲他想要一個可變版本?如果該字段是最終的,則不能設置,但可變版本需要設置字段。 – maress 2012-04-27 18:14:49

+0

儘管只有'final'類'Foo'可以「保證」Foo的一個實例不會真的成爲某個邪惡的可變派生'Foo'的實例,但是有些時候它可能有助於繼承,即使是抽象的,被指定爲不可變的類(意味着任何可變的派生類將被視爲「破碎」)。例如,在一些上下文中,定義一個'ImmutableMatrix'抽象類可能是有用的,它的派生包括'ImmutableArrayMatrix'(由與矩陣大小相同的數組支持),'ImmutableConstantMatrix'(由...支持... – supercat 2012-06-16 16:24:35

1

另一種選擇是使用與UnmodifiableList相同的策略。首先,創建一個指定類型的接口。

interface List<T>{ 
    List<T> add(T t); 
    T getAt(int i); 
    ... 
} 

然後你所有的業務邏輯實現你的可變類:

public class MutableList<T> implements List<T>{ 
    @Override 
    List<T> add(T t){ ... } 

    @Override 
    T getAt(int i){ ... } 

    ... 
} 

最後,創建不可變類是可變的一個景色。您可以實現相同的接口,但將所有讀取方法調用委託給查看的對象,並禁止使用異常進行任何寫入訪問。

public class UnmodifiableList<T> implements List<T>{ 
    //This guy will do all hard work 
    private List delegate; 

    public UnmodifiableList(List<? extends T> delegate){ 
     this.delegate = delegate; 
    } 

    //Forbidden mutable operation: throw exception! 
    @Override 
    List<T> add(T t){ 
     throw new UnsupportedOperationException("List is unmodifiable!"); 
    } 

    //Allowed read operation: delegate 
    @Override 
    T getAt(int i){ 
     return delegate.getAt(i); 
    } 

    ... 
} 

這種方法,你實現業務邏輯只有一次,可以先用自己的方法和驗證檢查把它變成一個imutable對象之前建立一個對象的利益。

+0

重要的是要注意提供潛在可變對象的只讀視圖的對象與提供它們自己創建的對象的只讀視圖的對象之間的區別,並且從不暴露於可能會改變它的任何上下文中。 「不可修改」一詞在這方面有點模糊。我更喜歡像「ReadableFoo」這樣的術語來指稱已知可讀且不能被直接修改的東西,但可以通過其他方式進行更改,而「ImmutableFoo」則指代保證永不改變的事物。 「不可修改」似乎是一個奇怪的中間地帶。 – supercat 2012-05-02 14:46:59

+0

是的,你是對的。原始名單的持有人可能會改變其內容,並且會暴露於該觀點。另一方面,一個視圖更加高效,計算和記憶明智。正確的工具取決於什麼是使用,不幸的是提問者沒有提供關於它的很多信息...... – 2012-05-02 16:33:32