具有非最终成员的不可变 Java class
Immutable Java class with non-final member
我在理解 Java 中的不变性思想方面仍然存在一些问题。我知道它不同于 C++ 中的 const
-ness 并且 final
class 只有 classes 的 final
成员本身是不可变的不可变的。例如。以下 class 是不可变的:
public final class A {
final String x;
final int y;
public A(String x, String y) {
this.x = x;
this.y = y;
}
}
除了 here 和其他地方的类似内容之外,是否还有一些正式的定义?
考虑以下示例。 Person
是不可变的吗?除了使成员 mother
和 father
final
之外,有没有办法让它不可变?我不能使它们 final
因为我必须从具有任意排序的输入文件构建一个 People
对象的列表,并且不想对此输入执行拓扑排序。另外,应该可以表示循环的情况。
public final class Person {
Person father = null;
Person mother = null;
public final String name;
Person(String name) { this.name = name; }
public Person getFather() { return father; }
public Person getMother() { return mother; }
}
// in the same package
public class TrioBuilder {
// build trio of child, mother, and father
public static ArrayList<Person> build(String c, String m, String f) {
Person child = new Person(c);
Person mother = new Person(m);
Person father = new Person(f);
child.father = father;
child.mother = mother;
ArrayList<Person> result = new ArrayList<Person>();
result.add(child);
result.add(mother);
result.add(father);
return result;
}
}
很简单:class 是不可变的,只要你创建它的实例,
您不能更改该实例的内部 state/data。你是否实现了
使用 final
或其他一些机制是另一个问题。
Is Person immutable?
不,不是。
不可变 class 是 final
并且只有 final
个成员。
在你的情况下,你想要使用的是一个生成器 class:
final Person person = new PersonBuilder().withFather(xx).withMother(xx).build();
这样你可以让 Person
的所有成员成为 final
,并且由于 Person
本身是最终的,你得到一个真正不可变的 class.
定义不可变对象的策略Java Doc
Don't allow subclasses to override methods. The simplest way to do
this is to declare the class as final. A more sophisticated approach
is to make the constructor private and construct instances in factory
methods.
Java 本身定义了你必须使 class final
来创建不可变的 class.
在 Java 中,final
关键字阻止此 class 的任何子class。所以 subclass 不可能自己定义可变字段。您的第一个示例是不可变的,因为没有修改字段值的方法。
除此之外,在 Java 中 Unsafe
甚至可以修改 final 字段,因此没有像其他语言那样的编译器不变性。
您的 Person
class 的不可变版本可能如下所示:
public final class Person {
public final Person father;
public final Person mother;
public final String name;
Person(final String name) {
this(name, null, null);
}
Person(final String name, final Person father, final Person mother) {
this.name = name;
this.father = father;
this.mother = mother;
}
public Person setFather(final Person father) {
return new Person(name, father, this.mother);
}
public Person getFather() {
return father;
}
public Person setMother(final Person mother) {
return new Person(name, father, mother);
}
public Person getMother() {
return mother;
}
public static void main(final String... args) {
final Person p1 = new Person("Foo").setMother(new Person("Bar")).setFather(new Person("Baz"));
System.out.println(p1);
}
@Override
public String toString() {
return "Person{" +
"father=" + father +
", mother=" + mother +
", name='" + name + '\'' +
'}';
}
}
来自Effective Java 2nd edition, by Joshua Bloch
:
要使 class 不可变,请遵循以下五个规则:
不要提供任何修改对象状态的方法(称为修改器)。
确保class不能扩展。这可以防止粗心或恶意的 subclasses 通过表现得好像对象的状态已经改变来损害 class 的不可变行为。防止 subclassing 通常是通过将 class 设置为 final 来实现的,但是还有一个替代方法。
使所有字段成为最终字段。
将所有字段设为私有。
确保对任何可变组件的独占访问。
点 1
和 3
到 5
是不言自明的。 2
点解释了为什么允许 class 扩展会影响您的可变性(或不影响)。 FWIW,他在 2
中建议的替代方案是使构造函数 private
,因此没有 class 可以扩展它(对于 class 扩展另一个,调用 super
应该制作构造函数,在这种情况下不能这样做)使其有效地不可扩展。
A class 可以 是不可变的,无论它的属性是否声明为 final 如果您不提供任何在构造后修改属性的方法,那么 class 是不可变的。嗯,大体上是这样,但是有一些问题需要注意:
显然,属性需要是私有的,以防止它们被直接访问或被保护字段的子class更改。
根据您是否激活了安全管理器,可能仍然可以使用反射包修改属性以直接访问它们。 serialization/deserialization 的一些技巧也可能会导致意想不到的事情发生。
未声明为 final 的属性可能存在并发问题 - Java 不保证创建对象的线程以外的线程将看到非最终属性的正确值,除非同步使用。
在 属性 引用另一个对象的情况下,被引用对象的内部状态当然可能会发生变化,除非它也是不可变的。
如果 class 没有被声明为 final 那么它可以被 subclassed 和 getters 覆盖,这可能会使属性看起来已经改变,尽管原来的属性保持不变他们的价值观。
我在理解 Java 中的不变性思想方面仍然存在一些问题。我知道它不同于 C++ 中的 const
-ness 并且 final
class 只有 classes 的 final
成员本身是不可变的不可变的。例如。以下 class 是不可变的:
public final class A {
final String x;
final int y;
public A(String x, String y) {
this.x = x;
this.y = y;
}
}
除了 here 和其他地方的类似内容之外,是否还有一些正式的定义?
考虑以下示例。 Person
是不可变的吗?除了使成员 mother
和 father
final
之外,有没有办法让它不可变?我不能使它们 final
因为我必须从具有任意排序的输入文件构建一个 People
对象的列表,并且不想对此输入执行拓扑排序。另外,应该可以表示循环的情况。
public final class Person {
Person father = null;
Person mother = null;
public final String name;
Person(String name) { this.name = name; }
public Person getFather() { return father; }
public Person getMother() { return mother; }
}
// in the same package
public class TrioBuilder {
// build trio of child, mother, and father
public static ArrayList<Person> build(String c, String m, String f) {
Person child = new Person(c);
Person mother = new Person(m);
Person father = new Person(f);
child.father = father;
child.mother = mother;
ArrayList<Person> result = new ArrayList<Person>();
result.add(child);
result.add(mother);
result.add(father);
return result;
}
}
很简单:class 是不可变的,只要你创建它的实例,
您不能更改该实例的内部 state/data。你是否实现了
使用 final
或其他一些机制是另一个问题。
Is Person immutable?
不,不是。
不可变 class 是 final
并且只有 final
个成员。
在你的情况下,你想要使用的是一个生成器 class:
final Person person = new PersonBuilder().withFather(xx).withMother(xx).build();
这样你可以让 Person
的所有成员成为 final
,并且由于 Person
本身是最终的,你得到一个真正不可变的 class.
定义不可变对象的策略Java Doc
Don't allow subclasses to override methods. The simplest way to do this is to declare the class as final. A more sophisticated approach is to make the constructor private and construct instances in factory methods.
Java 本身定义了你必须使 class final
来创建不可变的 class.
在 Java 中,final
关键字阻止此 class 的任何子class。所以 subclass 不可能自己定义可变字段。您的第一个示例是不可变的,因为没有修改字段值的方法。
除此之外,在 Java 中 Unsafe
甚至可以修改 final 字段,因此没有像其他语言那样的编译器不变性。
您的 Person
class 的不可变版本可能如下所示:
public final class Person {
public final Person father;
public final Person mother;
public final String name;
Person(final String name) {
this(name, null, null);
}
Person(final String name, final Person father, final Person mother) {
this.name = name;
this.father = father;
this.mother = mother;
}
public Person setFather(final Person father) {
return new Person(name, father, this.mother);
}
public Person getFather() {
return father;
}
public Person setMother(final Person mother) {
return new Person(name, father, mother);
}
public Person getMother() {
return mother;
}
public static void main(final String... args) {
final Person p1 = new Person("Foo").setMother(new Person("Bar")).setFather(new Person("Baz"));
System.out.println(p1);
}
@Override
public String toString() {
return "Person{" +
"father=" + father +
", mother=" + mother +
", name='" + name + '\'' +
'}';
}
}
来自Effective Java 2nd edition, by Joshua Bloch
:
要使 class 不可变,请遵循以下五个规则:
不要提供任何修改对象状态的方法(称为修改器)。
确保class不能扩展。这可以防止粗心或恶意的 subclasses 通过表现得好像对象的状态已经改变来损害 class 的不可变行为。防止 subclassing 通常是通过将 class 设置为 final 来实现的,但是还有一个替代方法。
使所有字段成为最终字段。
将所有字段设为私有。
确保对任何可变组件的独占访问。
点 1
和 3
到 5
是不言自明的。 2
点解释了为什么允许 class 扩展会影响您的可变性(或不影响)。 FWIW,他在 2
中建议的替代方案是使构造函数 private
,因此没有 class 可以扩展它(对于 class 扩展另一个,调用 super
应该制作构造函数,在这种情况下不能这样做)使其有效地不可扩展。
A class 可以 是不可变的,无论它的属性是否声明为 final 如果您不提供任何在构造后修改属性的方法,那么 class 是不可变的。嗯,大体上是这样,但是有一些问题需要注意:
显然,属性需要是私有的,以防止它们被直接访问或被保护字段的子class更改。
根据您是否激活了安全管理器,可能仍然可以使用反射包修改属性以直接访问它们。 serialization/deserialization 的一些技巧也可能会导致意想不到的事情发生。
未声明为 final 的属性可能存在并发问题 - Java 不保证创建对象的线程以外的线程将看到非最终属性的正确值,除非同步使用。
在 属性 引用另一个对象的情况下,被引用对象的内部状态当然可能会发生变化,除非它也是不可变的。
如果 class 没有被声明为 final 那么它可以被 subclassed 和 getters 覆盖,这可能会使属性看起来已经改变,尽管原来的属性保持不变他们的价值观。