如何在不使用final关键字的情况下在Java中创建不可变类

时间:2011-03-23 09:30:12

标签: java

  

可能重复:
  Implement a final class without the “final” keyword

我想在不使用final关键字的情况下在Java中创建不可变类。

6 个答案:

答案 0 :(得分:11)

我认为smt应该可以正常工作

class Immutable {
    private int i;
    public static Immutable create(int i){
        return new Immutable(i);
    }
    private Immutable(int i){this.i = i;}
    public int getI(){return i;}
}

但最终是可取的。

答案 1 :(得分:4)

final关键字不会让你的类不可变。它将避免你的课程从另一个课程扩展。

public final class Foo {
   //....
}

public class Bar extends Foo {
   //COMPILATION ERROR!
}

如同在duffymo的回答中所看到的那样,一个充实的课程设计将使你的课堂不可改变。

请注意,您可以将final声明为将在构造函数中初始化的字段:

class Foo {
    private final int state

   public Foo(int v) {
      this.state=v;
   }

   //....
}

不同之处在于,虽然在duffymo示例中,值可以从内部例程更改(即,一个方法将一个值添加到值,一个计数器的类型),在我的示例中,您将无法执行此操作

让我们尽量避免使用final关键字:

public class Foo {

   private int state;


   private Foo(int v) {
       this.state=v;
   }

   public static Foo getInstance(int value) {
      return new Foo(value);
   }

}

您只能获取Foo访问Foo.getInstance方法的实例。

但无论如何,你可以扩展Foo类并使其可变 我错了。我不会编译,因为你可以加入Foo构造函数。

public class Bar extends Foo {
    private int ohNopes;

    //COMPILATION ERROR!
    public Bar(int v) {
        this.ohNopes=v; 
    }
}

所以,毕竟它似乎可以完成。

答案 2 :(得分:3)

不可变类不是最终的问题是,子类可能不是不可变的。

以下是Java API的示例,java.lang.String是不可变的和final,如果将字符串传递给您的某个方法,则可以确保它将保持一致状态。

以下内容无法编译,因为String是final:

public class MyString extends java.Lang.String {
    public MyString(String original) {
        Super(original);
    }

    @Override
    public String toString() {
        return String.valueOf(System.currentTimeMillis());
}

另一方面,java.ma.BigDecimal本身是不可变的,但它不是最终的,并且允许被子类化。这开辟了一系列问题。如果将BigDecimal传递给您的某个方法,则不能依赖于没有人像使用String那样覆盖BigDecimal的事实。 BigDecimal的子类可能会将其方法替换为其他方法,从而产生不可预测的结果。

以下编译,因为BigDecimal不是不可变的:

public class MyBigDecimal extends java.math.BigDecimal {
    public MyBigDecimal(double val) {
        super(val);
    }

    private int count = 0;
    // override intValue which changes the state of this instance
    @Override
    public int intValue() {
        return count++; 
    }

    // rinse and repeat for the rest of the BigDecimal methods... 
}

你不能依赖于传递给你代码的BigDecimal实例的状态,如果你需要依赖它们的不变性,你应该制作非最终类的防御副本。

答案 3 :(得分:1)

我无法想象为什么你反对使用final,但是这里有一个可以完成工作的类。我知道有关序列化和反思的细微之处,但如果没有特殊的恶作剧,这是不可能改变的:

public class Immutable
{
    private int value;

    public Immutable(int v)
    { 
        this.value = v;
    }

    public int getValue() { return this.value; }
}

答案 4 :(得分:0)

该类应在构造函数中设置其所有值,并且不提供setter(修改类成员的方法)。

答案 5 :(得分:-1)

您可以创建一个类,然后创建一个.jar并使用jar作为资源。