在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);
}
}
编辑:更准确地指定问题。
答案 0 :(得分:7)
您可以使用builder pattern。
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));
答案 1 :(得分:2)
您应该查看builder pattern。
答案 2 :(得分:2)
一个好的解决方案是让您的字段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
中的所有设置者返回Builder
实例,以便您可以轻松地链接设置者调用。这非常有用。
你可以看看Joshua Bloch提出的Builder模式。
正如我之前所说,结合验证框架(参见前{。{3}}),这非常强大。
答案 3 :(得分:2)
使用接口。这样做:
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
不可变的地方。
答案 4 :(得分:1)
在构造函数中初始化是实现不变性的最简单和最安全的方法,因为这是在不可变类中使用final
字段的唯一方法(这是标准习惯用法,并且具有有益效果,尤其是如果你的class用于多线程环境中)。如果你班上有很多属性,这可能表明它试图做太多。考虑将其划分为较小的类,或将相关属性组提取到复合属性类中。
使用Builder(带有私有构造函数)是可能的,但是它仍然需要一种方法来设置正在构建的对象的属性。因此,您将回到构造函数参数与访问私有成员的原始困境。在后一种情况下,您无法将正在构建的对象的属性声明为final
,其中恕我直言是一个很好的减号。在前一种情况下,您仍然可以首先使用相同长的构造函数参数列表。刚才有很多额外的样板代码。
答案 5 :(得分:0)
对所有实例变量使用final
个字段。如果您愿意,可以创建一个构造函数,并选择不公开setter,例如
public class Person {
private final String firstName;
....
public Person(String firstName, ... ) {
this.firstName = firstName;
}
}
答案 6 :(得分:0)
您可以通过创建只读接口然后将实现变为可变bean来实现“不可变”bean。绕过接口将不允许变异,但是当你构造对象并实现它时,你可以做各种各样的bean-y事情:
public interface Person {
String getFirstName();
String getLastName();
// ... other immutable methods ...
}
public class MutablePerson implements Person {
// ... mutable functions, state goes here ...
}
答案 7 :(得分:0)
使用工厂模式:
答案 8 :(得分:0)
final fields.
final public class Person
setXXX()
方法来设置值,因为它会更改变量的状态。但是允许使用getXXX()
方法。遵循上述不可变类的指南。