2012-04-27 97 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