[英]Possibilities of creating immutable class in Java
在Java中创建不可变bean的可能性有多大? 例如,我有不可变类Person
。 什么是创建实例和填充私有字段的好方法。 公共构造函数对我来说似乎并不好,因为当类在其他应用程序中增长时会出现很多输入参数。 谢谢你的任何建议。
public class Person {
private String firstName;
private String lastName;
private List<Address> addresses;
private List<Phone> phones;
public List<Address> getAddresses() {
return Collections.unmodifiableList(addresses);
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public List<Phone> getPhones() {
return Collections.unmodifiableList(phones);
}
}
编辑 :更准确地指定问题。
您可以使用构建器模式 。
public class PersonBuilder {
private String firstName;
// and others...
public PersonBuilder() {
// no arguments necessary for the builder
}
public PersonBuilder firstName(String firstName) {
this.firstName = firstName;
return this;
}
public Person build() {
// here (or in the Person constructor) you could validate the data
return new Person(firstName, ...);
}
}
然后你可以像这样使用它:
Person p = new PersonBuilder.firstName("Foo").build();
乍一看,它可能看起来比具有大量参数的简单构造函数更复杂(并且可能是),但是有一些显着的优点:
Person
类和构建器,而无需声明多个构造函数或需要重写创建Person
每个代码:只需向构建器添加方法,如果有人不调用它们,则无关紧要。 Person
不同参数。 您可以使用构建器创建多个类似的Person
对象,这对单元测试很有用,例如:
PersonBuilder builder = new PersonBuilder().firstName("Foo").addAddress(new Address(...)); Person fooBar = builder.lastName("Bar").build(); Person fooBaz = builder.lastName("Baz").build(); assertFalse(fooBar.equals(fooBaz));
您应该看一下构建器模式 。
一个好的解决方案是让你的字段final
,将你的构造函数添加为private
并在你的代码中使用Builders
。 在我们的项目中,我们将Builder
模式与验证框架结合起来,这样一旦创建了一个对象,我们就确定它是不可变的和有效的。
这是一个简单的例子:
public class Person {
public static class Builder {
private String firstName;
private String lastName;
private final List<String> addresses = new ArrayList<String>();
private final List<String> phones = new ArrayList<String>();
public Person create() {
return new Person(firstName, lastName, addresses, phones);
}
public Builder setFirstName(String firstName) {
this.firstName = firstName;
return this;
}
public Builder setLastName(String lastName) {
this.lastName = lastName;
return this;
}
public Builder addAddresse(String adr) {
if (adr != null) {
addresses.add(adr);
}
return this;
}
public Builder addPhone(String phone) {
if (phone != null) {
phones.add(phone);
}
return this;
}
}
// ************************ end of static declarations **********************
private final String firstName;
private final String lastName;
private final List<String> addresses;
private final List<String> phones;
private Person(String firstName, String lastName, List<String> addresses, List<String> phones) {
this.firstName = firstName;
this.lastName = lastName;
this.addresses = addresses;
this.phones = phones;
}
public List<String> getAddresses() {
return Collections.unmodifiableList(addresses);
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public List<String> getPhones() {
return Collections.unmodifiableList(phones);
}
}
在我的示例中,您可以看到Builder
中的所有setter都返回Builder
实例,以便您可以轻松地链接setter调用。 这非常有用。
你可以看一下Joshua Bloch提出的Builder模式。
正如我之前所说,结合验证框架(参见http://www.hibernate.org/subprojects/validator.html ),这非常强大。
带接口。 做这个:
public interface Person {
String getFirstName();
String getLastName();
// [...]
}
你的实施:
// PersonImpl is package private, in the same package as the Factory
class PersonImpl {
String getFirstName();
void setFirstName(String s);
String getLastName();
void setLastName(String s);
// [...]
}
// The factory is the only authority to create PersonImpl
public class Factory {
public static Person createPerson() {
PersonImpl result = new PersonImpl();
// [ do initialisation here ]
return result;
}
}
永远不要将实现暴露给你希望Person
不可变的地方。
尽管如此,在构造函数中初始化是实现不变性的最简单和最安全的方法,因为这是在不可变类中使用final
字段的唯一方法(这是标准习惯用法,并且具有有益效果,特别是如果您的类在多线程环境中使用)。 如果你班上有很多属性,这可能表明它试图做太多。 考虑将其划分为较小的类,或将相关属性组提取到复合属性类中。
使用Builder(带有私有构造函数)是可能的,但是它仍然需要一种方法来设置正在构建的对象的属性。 因此,您将回到构造函数参数与访问私有成员的原始困境。 在后一种情况下,你不能声明正在构建的对象的属性为final
,恕我直言是一个很好的减号。 在前一种情况下,您仍然可以首先使用相同长的构造函数参数列表。 刚才有很多额外的样板代码。
对所有实例变量使用final
字段。 如果您愿意,可以创建构造函数并选择不公开setter,例如,
public class Person {
private final String firstName;
....
public Person(String firstName, ... ) {
this.firstName = firstName;
}
}
您可以通过创建只读接口然后将实现变为可变bean来实现“不可变”bean。 传递接口不允许变异,但是当你构造对象并实现它时,你可以做各种各样的bean-y事情:
public interface Person {
String getFirstName();
String getLastName();
// ... other immutable methods ...
}
public class MutablePerson implements Person {
// ... mutable functions, state goes here ...
}
使用工厂模式:
final fields.
final public class Person
使类成为“最终”类 setXXX()
方法来设置值,因为它会改变变量的状态。 但是允许getXXX()
方法。 遵循上面的Immutable类指南。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.