1. 问题的源由
在J2EE项目开发中,会涉及很多领域模型对象,例如,
- VO (View Object) 视图对象,也叫展示对象,用于前端页面渲染所需要的数据
- DTO (Data Transfer Object) 数据传输对象,一般用于Service和Manager向外传输数据
- PO (Persistent Object) 持久化对象,一般和数据库表结构会形成一一映射关系,通过DAO层向上传输数据源对象
更多领域分层模型,及其所在的应用分层,可见阿里巴巴Java开发手册中的工程规约定义。
这些VO/DTO/PO都是POJO对象,每个属性都有getter/setter方法的定义,其数据转换链条大概为,
数据库DB 》PO 》DTO 》VO
可以看到,在项目中不同的应用分层DB/DAO/Service/Manager/Web,经常会需要把一个数据对象转换为其它类型的数据对象,转换的同时复制部分对象属性,有时部分属性需要从多个数据对象获取进行组装。这些数据的转换有一个共同的需求,那就是从一个源对象变为目标对象
源对象 Source s 》目标对象 Target t
接下来我们就需要讨论这个转换过程。
2. 单个对象的转换
1)简单的Java对象转换,使用BeanUtils.copyProperties
使用Spring都知道Spring Beans中提供了一个BeanUtils,可以用于对象的属性复制,其使用方法为,
Target t = new Target();
BeanUtils.copyProperties(s, t);
BeanUtils.copyProperties(s, t, 'password');
其中,第二个copyProperties方法中第三个参数是告知复制过程中忽略复制'password'这个属性。
2)单个对象的转换,指定目标类
BeanUtils.copyProperties不提供目标对象的创建,因此每次都需要new出一个目标对象。为了能够省去这一步,可以使用下面的方法。
实现代码
public static <S, T> T convert(S s, Class<T> clazz) {
T t = null;
try {
t = clazz.newInstance();
BeanUtils.copyProperties(s, t);
} catch (Exception e) {
e.printStackTrace();
}
return t;
}
上述方法将对象的生成封装起来返回。
使用样例
Target t = convert(s, Target.class);
使用中直接指定目标类,代码更加简洁易懂。
3. List/Set集合之间的转换
在很多时候,我们不仅处理一个对象的转换,而是面对集合的对象,这时需要能够对集合中的对象进行批量处理转换。
1)List集合转换,将列表中每一个实体转换为目标类
实现代码
public static <S, T> List<T> convert(Iterable<S> iterable, Class<T> clazz) {
return StreamSupport.stream(iterable.spliterator(), false)
.map(s -> convert(s, clazz))
.collect(Collectors.toList());
}
使用样例
List<Target> targets = convert(srcCollection, Target.class);
2)List集合转换,提供一个mapper转换方法,将列表中每一个实体对象转换为目标类
实现代码
public static <S, T> List<T> convert(Iterable<S> iterable,
Function<? super S, ? extends T> mapper) {
return StreamSupport.stream(iterable.spliterator(), false)
.map(mapper)
.collect(Collectors.toList());
}
使用样例
List<Target> targets = convert(srcCollection, mapper);
其中mapper的定义为Function<? super S, ? extends T>,即该函数需要一个源对象作为输入参数,返回一个目标对象
3)List集合转换,转换为指定的List/Set容器类,集合中的实体对象不变
实现代码
public static <S, U extends Collection<S>> U collect(Iterable<S> iterable, CollectionFactory<S, U> factory) {
U collection = factory.createCollection();
iterable.forEach(collection::add);
return collection;
}
其中CollectionFactory为一个集合类工厂。
使用样例
Iterable<Integer> iterable = IntStream.range(0, 5).boxed().collect(Collectors.toList());
ArrayList<Integer> arrayList = collect(iterable, ArrayList::new);
HashSet<Integer> hashSet = collect(iterable, HashSet::new);
LinkedList<Integer> linkedList = collect(iterable, LinkedList::new);
上述将一个Collection集合转换为ArrayList/HashSet/LinkedList,集合中的整型变量对象保持不变。
4)List集合转换,转换为指定的List/Set容器类,并将集合中的实体对象转换为目标类
实现代码
public static <S, U extends Collection<T>, T> U collect(Iterable<S> iterable, Class<T> clazz, CollectionFactory<T, U> factory) {
Iterable<T> list = convert(iterable, clazz);
U collection = factory.createCollection();
list.forEach(collection::add);
return collection;
}
使用样例
Iterable<UserEntity> users = UserDao.SingleInstance.findAll();
ArrayList<UserVO> arrayList = collect(users, UserVO.class, ArrayList::new);
HashSet<UserVO> hashSet = collect(users, UserVO.class, HashSet::new);
LinkedList<UserVO> linkedList = collect(users, UserVO.class, LinkedList::new);
上述将一个Collection集合转换为ArrayList/HashSet/LinkedList,同时集合中的UserEntity变量对象转换为了UserVO。
4. List/Set/Map之间的转换
Java中Map的处理效率比List/Set高效不少,有些时候需要在List/Set/Map之间进行转换。
1)List集合转换,转换为Map集合,提供一个keyGenerator生成key,实体对象保存为value
实现代码
public static <S> Map<String, S> map(Iterable<S> iterable, Function<? super S, String> keyGenerator) {
return StreamSupport.stream(iterable.spliterator(), false)
.collect(Collectors.toMap(keyGenerator, Function.identity()));
}
使用样例
List<UserEntity> users = UserDao.SingleInstance.findAll();
Map<String, UserEntity> result = map(users, UserEntity::getName);
上述样例中由UserEntity::getName来生成对象的键值,其必须为保证唯一。
2)Map集合转换,转换为List集合,实体对象保持不变
实现代码
public static <K, S, U extends Collection<S>> U collect(Map<K, S> map, CollectionFactory<S, U> factory) {
U collection = factory.createCollection();
map.values().iterator().forEachRemaining(collection::add);
return collection;
}
使用样例
Map<String, UserEntity> users;
List<UserEntity> list1 = collect(users, ArrayList::new);
Set<UserEntity> set = collect(users, HashSet::new);
List<UserEntity> list2 = collect(users, LinkedList::new);
Collection collection = users.values();
上述将一个用户的Map集合转换为List集合,可以看到一个简单获得List集合的方法就是使用map.values()方法。
3)Map集合转换,转换为指定的Map容器类
实现代码
public static <K, S, T, U extends Map<K, T>> U map(Map<K, S> srcMap, Class<T> clazz, MapFactory<K, T, U> factory) {
U u = factory.createMap();
srcMap.entrySet()
.forEach(ksEntry -> u.put(ksEntry.getKey(),
convert(ksEntry.getValue(), clazz)));
return u;
}
使用样例
Map<String, UserEntity> users;
Map<String, UserVO> m1 = map(users, UserVO.class, HashMap<String, UserVO>::new);
Map<String, UserVO> m2 = map(users, UserVO.class, LinkedHashMap<String, UserVO>::new);
Hashtable<String, UserVO> m3 = map(users, UserVO.class, Hashtable<String, UserVO>::new);
上述将一个用户的Map集合转换为List集合,可以看到一个简单获得List集合的方法就是使用map.values()方法。
5. 源代码
演示代码仓库地址:https://gitee.com/pphh/blog,可以通过如下git clone命令获取仓库代码,
git clone git@gitee.com:pphh/blog.git
本篇博文的程序代码样例在文件路径171201_java_object_copy\171201_java_object_copy_convert中,请使用JDK8及后续版本编译并运行项目代码。
6. 参考资料
- POJO阿里巴巴Java开发手册(2017版本):应用分层和领域模型PO/DTO/VO
- 百度百科:POJO简单Java对象