消费者建设者的陷阱

Fee*_*eco 8 java constructor java-8

看下面的代码:

class Person {

    String name;
    int age;

    Person(Consumer<Person> consumer) {
        consumer.accept(this);
    }

}
Run Code Online (Sandbox Code Playgroud)

正如你所看到的,我正在使用"消费者构造函数",所以我可以创建一个这样的人:

var person = new Person(p -> {
    p.name = "John";
    p.age = 30;
})
Run Code Online (Sandbox Code Playgroud)

似乎这种方法比构建器模式或所有参数构造函数要好得多.

自Java 8发布以来已经过去了4年,但没有人使用消费者构造函数(至少我以前没见过它).

我不懂为什么?这种方法有一些缺陷或局限吗?

我找到了一个,但我认为它不重要:

class AdvancedPerson extends Person {

    String surname;

    AdvancedPerson(Consumer<AdvancedPerson> consumer) {
        super(); // <-- what to pass?
        consumer.accept(this);
    }

}
Run Code Online (Sandbox Code Playgroud)

当然,我们可以创建一个无参数构造函数,Person并在AdvancedPerson消费者构造函数中调用它.但这是一个解决方案吗?

那你觉得怎么样?
使用消费者构造函数是否安全?
这是建筑商和所有参数构造者的替代品吗?为什么?

Bjö*_*aar 12

从我的角度来看,这既不安全也不优雅.反对这种方法有几个原因:关于它的最糟糕的事情是它不仅允许而且还迫使你在对象未初始化时让这个引用逃脱.这有几个严重的影响:

  • 消费者将引用处于中间构造函数调用中的对象.然后,消费者可以做各种邪恶的事情,例如被另一个子类重写的调用方法.
  • 此外,消费者可能能够看到处于部分状态的对象,例如,某些成员被初始化而其他成员未被初始化.当涉及多线程时,这可能会变得更糟.
  • 使用这种方法无法创建不可变对象,因为您必须允许使用者干涉正在构建的对象的内部.
  • 最后但并非最不重要的是,您承担了负责保证班级不变量对消费者的责任.这绝对是一个班级应该对自己负责的事情.

有关创建对象的最佳实践,请参阅有效java的第二章.


And*_*lko 7

好像我根本不控制初始化的过程.

我不知道已经设置了多少字段,它们的值是什么.我不知道消费者调用了哪些方法(以及有多少种方法).看起来我打破了封装规则.

我还揭露this了外部世界的参考.
呼叫者可以this随心所欲地做任何事情.我不会收到通知.多线程有一个称为"不正当出版物"的术语.我会在这里使用它.