Consumer构造器应用疑问:Java 8推出后为何鲜有人使用?
这问题问得太戳点了——我第一次看到这种写法的时候也觉得“哇,这比写一堆Builder代码简洁多了”,但确实很少在实际项目里见到,主要是因为它存在几个关键的短板,咱们一个个说:
可读性门槛高
大部分Java开发者已经习惯了传统的构造器、Builder或者Setter链式调用,看到new Person(p -> { ... })这种形式,第一反应可能会疑惑:这是在传回调做初始化?还是要执行什么后续逻辑?得花一秒钟转个弯才能明白是在给对象赋值。对比Builder的new PersonBuilder().name("John").age(30).build(),语义直接到不用动脑子,谁看都懂。编译时安全没保障
全参构造器会强制你按顺序传所有参数,Lombok的@Builder甚至能帮你检查必填字段有没有设置。但Consumer构造器完全靠开发者自觉——你要是忘了设置name或者age,编译器连个警告都不会给你,只能等运行时出问题才发现,这在大型团队协作项目里简直是埋雷。IDE支持跟不上
用Builder的时候,IDE会自动弹出所有可用的Setter方法提示,补全超顺畅;全参构造器也会提示参数类型和顺序。但在Consumer的lambda里,你敲p.的时候,IDE通常不会主动提示类里的字段(除非你手动触发),万一打错字段名,虽然编译器会报错,但整个开发体验比Builder差远了。和Java生态兼容性差
很多Java框架(比如Spring、JPA、Jackson)都是基于JavaBean规范(Setter/Getter)工作的,比如Spring的依赖注入、JPA的实体映射,都依赖这些规范。Consumer构造器初始化的对象如果没有对应的Setter,有些框架可能无法正常处理,或者需要额外配置。而Builder模式大多会遵循JavaBean规范,或者至少能和这些框架无缝配合。灵活性远不如Builder
Builder模式能做的事情太多了:设置默认值、参数校验(比如年龄不能小于0)、构建不可变对象(把字段设为final,Builder里赋值后build出不可变实例)、甚至支持复杂的对象组合。但Consumer构造器本质上只是在构造阶段允许你修改对象,对于不可变对象来说直接失效——如果Person的name和age是final的,你在lambda里根本没法赋值。
当然,这种写法也不是完全没用,比如在一些简单的测试场景、或者你想快速初始化一个临时对象的时候,偶尔用用确实省代码,但因为上面这些硬伤,它很难成为主流的对象初始化方式。
内容的提问来源于stack exchange,提问作者Boaris

