Java对象的类型转换和属性复制

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  T convert(S s, Class 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  List convert(Iterable iterable, Class clazz) {
  return StreamSupport.stream(iterable.spliterator(), false)
                      .map(s -> convert(s, clazz))
                      .collect(Collectors.toList());
}

使用样例

List targets = convert(srcCollection, Target.class);
2)List集合转换,提供一个mapper转换方法,将列表中每一个实体对象转换为目标类

实现代码

public static  List convert(Iterable iterable, 
           Function mapper) {
  return StreamSupport.stream(iterable.spliterator(), false)
                      .map(mapper)
                      .collect(Collectors.toList());
}

使用样例

List targets = convert(srcCollection, mapper);

其中mapper的定义为Function,即该函数需要一个源对象作为输入参数,返回一个目标对象

3)List集合转换,转换为指定的List/Set容器类,集合中的实体对象不变

实现代码

public static > U collect(Iterable iterable, CollectionFactory factory) {
  U collection = factory.createCollection();
  iterable.forEach(collection::add);
  return collection;
}

其中CollectionFactory为一个集合类工厂。

使用样例

Iterable iterable = IntStream.range(0, 5).boxed().collect(Collectors.toList());
ArrayList arrayList = collect(iterable, ArrayList::new);
HashSet hashSet = collect(iterable, HashSet::new);
LinkedList linkedList = collect(iterable, LinkedList::new);

上述将一个Collection集合转换为ArrayList/HashSet/LinkedList,集合中的整型变量对象保持不变。

4)List集合转换,转换为指定的List/Set容器类,并将集合中的实体对象转换为目标类

实现代码

public static , T> U collect(Iterable iterable, Class clazz, CollectionFactory factory) {
  Iterable list = convert(iterable, clazz);
  U collection = factory.createCollection();
  list.forEach(collection::add);
  return collection;
}

使用样例

Iterable users = UserDao.SingleInstance.findAll();
ArrayList arrayList = collect(users, UserVO.class, ArrayList::new);
HashSet hashSet = collect(users, UserVO.class, HashSet::new);
LinkedList 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  Map map(Iterable iterable, Function keyGenerator) {
  return StreamSupport.stream(iterable.spliterator(), false)
         .collect(Collectors.toMap(keyGenerator, Function.identity()));
}

使用样例

List users = UserDao.SingleInstance.findAll();
Map result = map(users, UserEntity::getName);

上述样例中由UserEntity::getName来生成对象的键值,其必须为保证唯一。

2)Map集合转换,转换为List集合,实体对象保持不变

实现代码

public static > U collect(Map map, CollectionFactory factory) {
  U collection = factory.createCollection();
  map.values().iterator().forEachRemaining(collection::add);
  return collection;
}

使用样例

Map users;
List list1 = collect(users, ArrayList::new);
Set set = collect(users, HashSet::new);
List list2 = collect(users, LinkedList::new);
Collection collection = users.values();

上述将一个用户的Map集合转换为List集合,可以看到一个简单获得List集合的方法就是使用map.values()方法。

3)Map集合转换,转换为指定的Map容器类

实现代码

public static > U map(Map srcMap, Class clazz,  MapFactory factory) {
  U u = factory.createMap();
  srcMap.entrySet()
        .forEach(ksEntry -> u.put(ksEntry.getKey(),
                          convert(ksEntry.getValue(), clazz)));
  return u;
}

使用样例

Map users;
Map m1 = map(users, UserVO.class, HashMap::new);
Map m2 = map(users, UserVO.class, LinkedHashMap::new);
Hashtable m3 = map(users, UserVO.class, Hashtable::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对象