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

时间:2019-01-21 16:48:37

标签: java generics lambda java-8 method-reference

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

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。而且我想将其用于不同的类型,不仅是字符串,还可以是整数,等等。

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

2 个答案:

答案 0 :(得分:12)

使用类似

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仍然不是适合此任务的数据结构,因为对于这些功能,无法执行有意义的查找,因此,该数据结构仅旨在进行迭代。

您可以将“仅在非null时复制”逻辑与通用辅助方法集成在一起:

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

答案 1 :(得分: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"
    ]
*/