请查看以下代码:
class Person {
String name;
int age;
Person(Consumer<Person> consumer) {
consumer.accept(this);
}
}
正如您所看到的,我正在使用&#34;消费者构造函数&#34;,所以我可以创建一个这样的人:
var person = new Person(p -> {
p.name = "John";
p.age = 30;
})
似乎这种方法比构建器模式或所有参数构造函数要好得多。
自Java 8发布以来已经过去了4年,但没有人使用消费者构建器(至少我以前没见过它)。我不明白为什么?这种方法有一些缺陷或局限吗?
我找到了一个,但我认为这不重要:
class AdvancedPerson extends Person {
String surname;
AdvancedPerson(Consumer<AdvancedPerson> consumer) {
super(); // <-- what to pass?
consumer.accept(this);
}
}
当然,我们可以在Person
中创建一个无参数构造函数,并在AdvancedPerson
使用者构造函数中调用它。但这是一个解决方案吗?
那你觉得怎么样? 使用消费者构造者是否安全? 这是建筑商和所有参数构造者的替代品吗?为什么呢?
答案 0 :(得分:12)
从我的角度来看,这既不安全也不优雅。反对这种方法有几个原因:最糟糕的是它不仅允许而且还迫使你在对象未初始化时让这个引用逃脱。这有几个严重的影响:
有关创建对象的最佳实践,请参阅有效java的第二章。
答案 1 :(得分:7)
似乎我根本无法控制初始化过程。
我不知道已经设置了多少字段,它们的值是多少。我不知道消费者打过哪种方法(以及有多少种方法)。看起来我打破了封装规则。
我还公开了外部世界的this
参考
调用者可以随心所欲地使用this
。而且我没有得到通知。多线程有一个称为“不正当出版物”的术语。我会在这里使用它。
答案 2 :(得分:4)
消费者构造函数似乎在Java中提供了一种简洁的方式来编写代码,在其他语言中这些代码将被称为&#34;属性&#34;。只需传入初始化对象状态的Consumer
。
但这不适用于正确的类设计,包括封装。在这里,这意味着name
和age
应为private
。这意味着Consumer
不再能够访问实例变量并且不再编译。
封装和构建器模式都具有检查和拒绝对象的无效状态的优势。
如果Java具有属性的概念,那么现实中的obj.prop1 = value;
和variable = obj.prop2;
等代码称为方法,那么这不是一个问题。但它没有这个概念。
您找到的陷阱可以通过为每个超类以及相关类传递Consumer
来解决,例如:
AdvancedPerson(Consumer<Person> pConsumer, Consumer<AdvancedPerson> consumer)
{
super(pConsumer); // <-- what to pass?
consumer.accept(this);
}
但是,这意味着用户必须为每个超类传递不同的Consumer
,这没有多大意义。
此外,从构造函数传递this
不是一个好主意;这是一个实例泄漏,其中外部代码可以访问尚未完全构建的对象。
封装,this
泄漏以及类层次结构中多个Consumer
的复杂化都是不使用消费者构造函数的好理由。