java - 消费者构造函数陷阱

标签 java constructor java-8

看下面的代码:

class Person {

    String name;
    int age;

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

}

如您所见,我正在使用“消费者构造函数”,因此我可以创建一个这样的人:

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 消费者构造函数中调用它。但这是解决方案吗?

那你怎么看呢?
使用消费者构造函数安全吗?
这是构建器和全参数构造器的替代品吗?为什么?

最佳答案

在我看来,它既不安全也不优雅。有几个理由反对这种方法:最糟糕的是,它不仅允许而且还强制您在对象尚未初始化时让 this 引用转义。这有几个严重的不良影响:

  • 消费者将有一个对象的引用,该对象处于中间构造函数调用中。然后,消费者可以做各种邪恶的事情,例如调用被另一个子类覆盖的方法。
  • 消费者也可以看到部分状态的对象,例如一些成员初始化,而其他成员则没有。当涉及多线程时,情况会变得更糟。
  • 无法使用这种方法创建不可变对象(immutable对象),因为您必须允许消费者干预正在构建的对象的内部结构。
  • 最后但并非最不重要的一点是,您将负责保证类不变量的负担交给了消费者。这绝对是一个类应该自己负责的事情。

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

关于java - 消费者构造函数陷阱,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49637637/

相关文章:

java - 处理复杂数据结构的正确方法是什么?

c# - 是否可以覆盖 C# 中的构造函数?

javascript - JS 文件中的函数不是由 index.js click evt 触发的

java - 带有 Consumer 或 Filter 的 Java8 List 的条件,哪种方式更好

Java 1.8u20 无法启动 spring boot 和 groovy 2.3.7

java - POI : setCellType(Cell. CELL_TYPE_FORMULA) 由于 Cell.CELL_TYPE_ERROR 而失败

java - 如何将 proto3 消息序列化为字符串并返回?

java - 不选择 JTable 中的行

c++ - 原始数据类型的构造函数

java - 当 API 说接口(interface)的方法返回某些内容时,它是什么意思?