0

为了在 Java 中使对象不可变,我将类标记为 final,它的所有变量都标记为 final,并且没有提供 setter 和 getter。这些是否会提供足够的保证来保证对象不会被修改?是全部 3 个必要条件还是 3 个条件中的 2 个就足够了?

4

5 回答 5

6
public final class MyClass

Has nothing to do with immutability, it only disallows inheritance.

Just marking variable references final is not enough, every object you refer to has to be immutable as well.

final doesn't make an object immutable, it makes the references immutable;

private final List<String> strings = new ArrayList<String>();

strings is still a mutable List, only the reference to the List is immutable.

Be careful with things like:

Collections.unmodifiableList(strings);

Collections.unmodifiableList() JavaDoc provides a "unmodifiable view" but it still doesn't guarantee that the underlying list could not be changed by an external reference to the original List that is being wrapped. A deep copy of the list contents would have to be made into a new list and that list wrapped with unmodifiable.

And every instance of every object and all their children and children's children have to be immutable as well.

于 2013-04-13T23:49:33.587 回答
1

将您的字段标记为最终选项是您在上述集合中唯一需要的选项。您可以提供 getter,但要小心像集合这样的可变子对象。Final 使引用不可变,但内容不可变。如果值是可变的,则使用 getter 的一个有用技术是制作一个防御性副本,如下所示:

public class ImmutableExample{

    private final int value1; // immutable
    private final List<Integer> value2; // contents will not be immutable

    public ImmutableExample(...){...} // be careful here to copy the collection as you want to disalow any outside modification.

    public int getValue1(){
        return value1;
    }

    public List<Integer> getValue2(){
        return Collections.unmodifiableList(value2);
    }
}

另一种选择是使用包含不可变集合的Google Guava集合,正如我们在此处讨论的: 不可变对象和不可修改集合。这些导致非常简单的不可变类:

public class ImmutableExample{

    private final int value1; // immutable
    private final ImmutableList<Integer> value2; // immutable

    public ImmutableExample(...){...}

    public int getValue1(){
        return value1;
    }

    public List<Integer> getValue2(){
        return value2;
    }
}
于 2013-04-13T23:53:23.807 回答
0

将所有变量设置为私有并仅提供 getter 会更自然。然后,您无需设置final不必要的类。

如果字段本身是非最终对象(例如集合),则必须进一步考虑。您应该只存储这些副本。

于 2013-04-14T00:06:29.523 回答
0

Marking your class as final you're only telling that no one can subclass it.

That said, marking your variables as final would be enough, as long as your class had only 'primitive' attributes. It's ok (and encouraged) to keep the getters, however, you also may want remove the setters, since while you don't want any external class to mess around with your variables, setters won't be needed.

于 2013-04-13T23:45:30.953 回答
-1

与这里的一些评论相反,使类 final确实与不变性有关。对于一个不可变的类,它应该是最终的,以便不允许子类覆盖任何 getter,从而不允许它们通过返回不同的值来“改变”它。

Recall that to guarantee immutability, a class must not permit itself to be subclassed.

- Joshua Bloch, Effective Java

此外,来自 Java 教程,定义不可变对象的策略

3.不允许子类覆盖方法。最简单的方法是将类声明为 final

于 2013-04-14T00:50:35.793 回答