通过getter和setter通用的方式更新pojo对象

8
假设我有一个包含不同类型的getter和setter的POJO对象。我想编写一些通用算法,基于只定义通过lambda表达式获取和设置器,来更新数据从一个到另一个。 我试图这样创建它:
private static final Map<Function<Entity, Object>, BiConsumer<Entity, Object>> ACCESSORS = new HashMap
        <Function<Entity, Object>, BiConsumer<Entity, Object>>() {{
    put(Entity::getAreaCode, Entity::setAreaCode);
}});

然后我遍历所有条目,将目标实体应用于它们,并且如果getter的结果不为null,则希望为其他实体应用相应的setter。

但是这不起作用,因为Object无法转换为String。而且我想将其用于不仅仅是String,还有Integers等不同类型...

是否可以在不创建特殊转换器并将其与每个条目关联的情况下通过一些简单的方法解决?


? 替换 Object。这样可以吗? - Sweeper
@Sweeper 不行。依然是关于无法将对象转换为整数/字符串的相同错误... - user_x
2个回答

13

使用类似以下的方式

private static final List<BiConsumer<Entity,Entity>> ACCESSORS =
    Collections.unmodifiableList(Array.asList(
        (src,dst) -> dst.setAreaCode(src.getAreaCode()),
        (src,dst) -> dst.setOtherProperty(src.getOtherProperty())
        /* etc */
));

然后,您可以循环遍历该列表并将每个操作应用于两个实体,例如

static final void copyAll(Entity src, Entity dst) {
    ACCESSORS.forEach(op -> op.accept(src, dst));
}
关键点在于实际的属性值类型由每个BiConsumer单独处理,但不再是泛型签名的一部分,因此无需为ACCESSORS声明。这甚至更有效,因为它可以处理原始数据类型而无需装箱开销。 Map本来就不是这个任务的合适数据结构,因为对于这些函数,没有进行有意义的查找,所以这个数据结构只用于迭代。
您可以使用通用辅助方法将“仅在非空时复制”逻辑集成到代码中:
private static final List<BiConsumer<Entity,Entity>> ACCESSORS =
    Collections.unmodifiableList(Arrays.asList(
        copyWhenNonNull(Entity::getAreaCode, Entity::setAreaCode),
        copyWhenNonNull(Entity::getOtherProperty, Entity::setOtherProperty)
        /* etc */
));
private static <E,V> BiConsumer<E,E> copyWhenNonNull(
    Function<? super E, ? extends V> getter, BiConsumer<? super E, ? super V> setter) {
    return (src,dst) -> {
        V value = getter.apply(src);
        if(value != null) setter.accept(dst, value);
    };
}

copyAll 方法不会发生变化。这甚至允许混合无条件复制永远不会是 null 的属性与有条件的复制。


谢谢,差不多就是那样) 我只是想要一个跳过空值更新的通用逻辑。 - user_x
@Holger 这太棒了(有些地方使用Dozer实现相同的功能感到羞愧) - Eugene
1
我甚至建议创建一个名为copy的函数,其签名与copyWhenNonNull相同,以便能够一致地使用方法引用,但这可能只是个人口味的问题... - Marco13
1
@Marco13 有很多种复制方法,比如 copyOrDefault(getter, setter, defaultValue) 或者 copyWhenMatching(getter, setter, predicate) 等等。 - Holger

0

我知道你已经有了答案,但是对于未来需要类似功能的人:我已经开发了一个围绕这个上下文的小型库 - datus

以下是一个示例,展示了它的一些特点:

class Person {
  //getters + setters omitted for brevity
  private String firstName;
  private String lastName;
}

class PersonDTO {
  //getters + setters + empty constructor omitted for brevity
  private String firstName;
  private String lastName;
}

  //the mutable API defines a mapping process by multiple getter-setter steps
  Mapper<Person, PersonDTO> mapper = Datus.forTypes(Person.class, PersonDTO.class).mutable(PersonDTO::new)
      .from(Person::getFirstName).into(PersonDTO.setFirstName)
      .from(Person::getLastName)
      .given(Objects::nonNull, ln -> ln.toUpperCase()).orElse("fallback")
      .into(PersonDTO::setLastName)
      .from(/*...*/).into(/*...*/)
      .build();

  Person person = new Person();
person.setFirstName("firstName");
    person.setLastName(null);
    PersonDTO personDto = mapper.convert(person);
/*
    personDto = PersonDTO [
        firstName = "firstName",
        lastName = "fallback"
    ]
*/
    person.setLastName("lastName");
    personDto = mapper.convert(person);
/*
    personDto = PersonDTO [
        firstName = "firstName",
        lastName = "LASTNAME"
    ]
*/

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接