为了账号安全,请及时绑定邮箱和手机立即绑定

如何实现通用的对象填充方法

如何实现通用的对象填充方法

幕布斯6054654 2019-03-01 10:41:19
先上代码: User对象 public class UserDTO { private int userId; private String userName; private int orgId; private OrgDTO orgDTO; } Org对象 public Class OrgDTO { private int orgId; private String orgName; } 工具类方法 public static <S, K, T> void oneToOne(List<S> sourceList, List<T> detailList, SafeFunction<S, K> sourceKey, SafeFunction<T, K> detailKey) { Map<K, T> detailMap = detailList.stream().collect(Collectors.toMap(detailKey, Function.identity())); for (S s : sourceList) { K key = sourceKey.apply(s); T detail = detailMap.get(key); if (detail != null) { // TODO How to call s.setDetail() ? } } } 参数说明: sourceList: 待填充数组 detailList: 填充物数组 sourceKey: sourceList与detailList的关联键 detailKey: detailList的关联键转换函数 好吧,以上参数说明比较难懂(我也不知道要怎么描述)…… 举个场景例子:有一个UserDTO和OrgDTO,假设他们在数据表的关系是1对1,很多时候我们需要根据UserDTO里面的orgId查询对应的OrgDTO,然后根据orgId把OrgDTO填充进UserDTO里面。 传统的代码写法如下: public void fillOrgDTO(List<UserDTO> userDTOs) { Set<Integer> orgIds = userDTOs.stream().map(UserDTO::getOrgId).collect(Collectors.toSet()); List<OrgDTO> orgDTOs = orgDAO.findByIds(orgIds); Map<Integer, OrgDTO> orgId2OrgDTOMap = orgDTOs.stream().collect(Collectors.toMap(OrgDTO::getOrgId, Function.identity())); for (UserDTO userDTO : userDTOs) { Integer orgId = userDTO.getOrgId(); OrgDTO orgDTO = orgId2OrgDTOMap.get(orgId); if (orgDTO != null) { userDTO.setOrgDTO(orgDTO); } } } 我的想法是,有没有办法把以上这段代码抽象成一个通用的方法,这样就不用每次都写类似的代码。 然后就试着写出了oneToOne这个方法,不过去到TODO那里就不知道怎么写了。 理论上用反射可以做到(需要变更方法签名),但我不想用反射,因为不论从性能还是维护角度来看,反射都不是一个很好的选择。 之前也了解过Java8的Consumer和BiConsumer,但试了一下好像也没法实现我的需求,可能是我理解得不够。 所以,在此提出这个问题,看看大家有没有好的解决方案? PS:方法签名可以变更,代码可任意修改,UserDTO、OrgDTO可以实现任意接口。
查看完整描述

1 回答

?
慕容森

TA贡献1853条经验 获得超18个赞

首先建议尽量用JDK functional interfaces,如果可以。下面是代码:

public <S, T, K> void oneToOne(List<S> sourceList, List<T> detailList, Function<S, K> sourceKeyExtractor, Function<T, K> detailKeyExactor,
        BiConsumer<S, T> action) {
    // Make sure the keys in detail list are unique.
    Map<K, T> detailMap = detailList.stream().collect(Collectors.toMap(detailKeyExactor, Function.identity()));
    sourceList.stream().forEach(s -> action.accept(s, detailMap.getOrDefault(sourceKeyExtractor.apply(s), null)));
}

@Test
public void test_oneToOne() {
    List<UserDTO> userList = null; // TODO
    List<OrgDTO> orgList = null; // TODO
    oneToOne(userList, orgList, s -> s.getOrgId(), t -> t.getOrgId(), (s, t) -> s.setOrgDTO(t));
}

其次,是一个设计的问题:因为 sourceKeyExtractor, detailKeyExactor离它操作的对象源比较远,可能会混淆或至少要记得一个对应关系,如果重构一下,把sourceKeyExtractor移到前面一点,这样就一清二楚每个keyExtractor操作的对象源。

public <S, T, K> void oneToOne(List<S> sourceList, Function<S, K> sourceKeyExtractor, List<T> detailList, Function<T, K> detailKeyExactor,
        BiConsumer<S, T> action) {
    // Make sure the keys in detail list are unique.
    Map<K, T> detailMap = detailList.stream().collect(Collectors.toMap(detailKeyExactor, Function.identity()));
    sourceList.stream().forEach(s -> action.accept(s, detailMap.getOrDefault(sourceKeyExtractor.apply(s), null)));
}
查看完整回答
反对 回复 2019-03-01
  • 1 回答
  • 0 关注
  • 508 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信