Sza*_*ego 19 java field class immutability
我有一个Employees
像这样调用的不可变类:
public final class Employees {
private final List<Person> persons;
public Employees() {
persons = new LinkedList<Person>();
}
public List<Person> getPersons() {
return persons;
}
}
Run Code Online (Sandbox Code Playgroud)
如何让这个类保持不变?
我做了现场private
,并final
和我没有提供setter方法.这足以实现不变性吗?
Dav*_*INO 18
编辑的答案不仅解释了具有可变版本的情况Person
,而且还具有不可变版本的情况Person
.
你的课是可变的,因为你可以这样做:
Employees employees = new Employees();
employees.getPersons().add(new Person());
Run Code Online (Sandbox Code Playgroud)
请注意,如果您更改代码以创建不可变类,则不会将人员列表传递给构造函数,您将拥有一个非常有用的类,其中包含一个空的人员列表,因此我认为有必要将a传递List<Person>
给构造函数.
现在有两种情况,具有不同的实现:
Person
是不可改变的Person
是可变的场景1 - Person
是不可变的
您只需要在构造函数中创建persons参数的不可变副本.
您还需要创建final
类或至少方法getPersons
以确保没有人提供该getPersons
方法的可变覆盖版本.
public final class Employees {
private final List<Persons> persons;
public Employees(List<Person> persons) {
persons = Collections.unmodifiableList(new ArrayList<>(persons));
}
public List<Employees> getPersons() {
return persons;
}
}
Run Code Online (Sandbox Code Playgroud)
场景2 - Person
是可变的
您需要在方法中创建深层副本persons
getPersons
.
您需要在构造函数上创建一个深层副本persons
.
您还需要创建final
类或至少方法getPersons
以确保没有人提供该getPersons
方法的可变覆盖版本.
public final class Employees {
private final List<Persons> persons;
public Employees(List<Person> persons) {
persons = new ArrayList<>();
for (Person person : persons) {
persons.add(deepCopy(person)); // If clone is provided
// and creates a deep copy of person
}
}
public List<Employees> getPersons() {
List<Person> temp = new ArrayList<>();
for (Person person : persons) {
temp.add(deepCopy(person)); // If clone is provided
// and creates a deep copy of person
}
return temp;
}
public Person deepCopy(Person person) {
Person copy = new Person();
// Provide a deep copy of person
...
return copy;
}
}
Run Code Online (Sandbox Code Playgroud)
这部分答案是为了说明为什么personsParameter
传递给构造函数的深层副本可以创建可变版本Employees
:
List<Person> personsParameter = new ArrayList<>();
Person person = new Person();
person.setName("Pippo");
personsParameter.add(person);
Employees employees = new Employees(personsParameter);
// Prints Pippo
System.out.println(employees.getPersons().get(0).getName());
employees.getPersons().get(0).setName("newName");
// Prints again Pippo
System.out.println(employees.getPersons().get(0).getName());
// But modifiyng something reachable from the parameter
// used in the constructor
person.setName("Pluto");
// Now it prints Pluto, so employees has changed
System.out.println(employees.getPersons().get(0).getName());
Run Code Online (Sandbox Code Playgroud)
The*_*ind 17
不,这还不够,因为在java中,偶数引用都是按值传递的.因此,如果您的List's
引用转义(将在它们调用时发生get
),那么您的类不再是不可变的.
你有2个选择:
List
并在调用get时返回它.List
并将其返回(或List
用此替换原件,然后您可以安全地返回它而无需进一步包装)注意:您必须确保Person
是不可变或者为每个创建的防守副本Person
中List
答案可以在文档中找到 - 定义不可变对象的策略:
不要提供"setter"方法 - 修改字段引用的字段或对象的方法.
使所有字段成为最终和私有.
不允许子类重写方法.
如果实例字段包含对可变对象的引用,则不允许更改这些对象:
4.1.不要提供修改可变对象的方法.
4.2.不要共享对可变对象的引用.永远不要存储对传递给构造函数的外部可变对象的引用; 如有必要,创建副本并存储对副本的引用.同样,必要时创建内部可变对象的副本,以避免在方法中返回原始对象.
你可以做得更好
import java.util.Collections;
...
public List<Person> getPersons() {
return Collections.unmodifiableList( persons);
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
1368 次 |
最近记录: |