【问题标题】:Build an object from an existing one using lombok使用 lombok 从现有对象构建对象
【发布时间】:2018-04-14 15:19:29
【问题描述】:

假设我有一个像龙目岛注释的类

@Builder
class Band {
   String name;
   String type;
}

我知道我能做到:

Band rollingStones = Band.builder().name("Rolling Stones").type("Rock Band").build();

是否有一种简单的方法可以使用现有对象作为模板创建 Foo 对象并更改其中一个属性?

类似:

Band nirvana = Band.builder(rollingStones).name("Nirvana");

我在 lombok 文档中找不到这个。

【问题讨论】:

    标签: java lombok


    【解决方案1】:

    您可能还想使用 com.fasterxml.jackson.databind.ObjectMapper 复制对象

    @AllArgsConstructor
    @Setter
    class Band {
        String name;
        String type;
    }
    
    ObjectMapper objectMapper = new ObjectMapper(); //it's configurable
    objectMapper.configure( DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false );
    objectMapper.configure( SerializationFeature.FAIL_ON_EMPTY_BEANS, false );
    
    Band rollingStones = new Band("Rolling Stones", "Rock Band");
    
    Band nirvana = objectMapper.convertValue( rollingStones, Band.class);
    nirvana.setName("Nirvana");
    

    它可以很容易地封装在一些实用方法中,以便在整个项目中使用,例如ConvertUtils.clone(rollingStones, Band.class)

    【讨论】:

    • 我尝试了您的示例并且它可以工作(在添加@NoArgsConstructor 之后),但在我的真实代码中,convertValue 返回它作为参数获得的相同实例。显着的区别是我的类继承自父类。
    【解决方案2】:

    有没有一种简单的方法可以使用现有对象作为模板来创建 Foo 对象并更改它的 一个 属性? (强调我的

    如果你真的想改变单个属性,那么有一个更好更有效的方法:

    @With
    class Band {
       String name;
       String type;
    }
    
    Band nirvana = rollingStones.withName("Nirvana");
    

    凋灵不会产生垃圾,但它只能改变一个领域。要更改许多字段,您可以使用

    withA(a).withB(b).withC(c)....
    

    并产生大量垃圾(所有中间结果),但比 toBuilder 更高效、更自然。

    注意:旧版本的 lombok 使用了 @Wither 注释。见beginning of documentation

    【讨论】:

    • 真的会产生那么多垃圾吗?我认为除了您要替换的字段之外,这都是浅拷贝(依赖于这样的概念,即如果这是预期的,您将使对象已经不可变)。 “垃圾”主要是被丢弃的顶级对象的引用(尽管我猜很多原语也可能导致更多垃圾)。
    • @jm0 当然,这都是浅拷贝。我所说的“大量垃圾”是指n-1withSomething 的一系列n 调用的对象。一个对象需要几个字节加上每个引用 4 或 8 个字节加上每个基元 1 到 8 个字节。所以我们谈论的是每次调用几十个字节。通常没什么大不了的。
    【解决方案3】:

    您可以使用toBuilder 参数为您的实例提供toBuilder() 方法。

    @Builder(toBuilder=true)
    class Foo {
       int x;
       ...
    }
    
    Foo f0 = Foo.builder().build();
    Foo f1 = f0.toBuilder().x(42).build();
    

    来自the documentation

    如果使用@Builder 生成构建器来生成您自己的类的实例(这种情况总是如此,除非将@Builder 添加到不返回您自己的类型的方法中),您可以使用@Builder(toBuilder = true)在您的类中生成一个名为 toBuilder() 的实例方法;它会创建一个新的构建器,从这个实例的所有值开始。

    免责声明:我是龙目岛开发者。

    【讨论】:

    • @Mustafa 还有@Wither,对于单字段更改效率更高:Foo f1 = f0.withX(42)
    • @maaartinus 我认为@Wither 生成的 with* 方法总是返回一个新对象,而不是设置现有对象的字段。这是低效率的。
    • @MGhostSoft 我显然假设创建一个新对象是目标。因此我们很常见,因为不可变对象被越来越多地使用。 ++× 对于更改单个字段,@Wither 是最好的。超过两个,toBuilder 获胜。请参阅下面的答案。
    • 对于零字段(即没有任何更改的对象副本),@Wither 根本不起作用,但 .toBuilder().build() 会。
    • 完美答案!
    猜你喜欢
    • 2013-11-18
    • 2017-10-24
    • 1970-01-01
    • 2018-11-01
    • 2015-09-24
    • 1970-01-01
    • 2016-01-04
    • 2013-11-30
    相关资源
    最近更新 更多