通过 getter 和 setter 更新 pojo 的通用方法

Generic way to update pojos via getters and setters

假设我的 POJO 具有 getter 和 setter 不同类型。我想编写一些通用算法,用于根据仅通过 lambda 定义 getters 和 setters 将数据从一个更新到另一个。 我正在尝试以这种方式创建它

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 的结果不为空,那么我想为其他实体应用相应的 setter。

但它不会工作,因为 Object 不能转换为 String。我想将它用于不同的类型,不仅是字符串,还有整数等...

是否可以通过一些简单的方法解决而无需创建特殊转换器并将其与每个条目相关联?

使用类似

的东西
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 中处理,但不再是通用签名的一部分,因此不需要为 [=14 声明=].它甚至更高效,因为它可以处理原始数据类型而无需装箱开销。

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 的属性的无条件复制与条件复制混合。

我知道你已经有了答案,但对于将来需要这样的东西的人:我已经围绕这个上下文开发了一个小型库 - 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"
    ]
*/