我想在不使用final
关键字的情况下在Java中创建不可变类。
答案 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作为资源。