Java自定义copyProperties,实现不同对象的相同属性(包含子对象)赋值

Posted £漫步 云端彡

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java自定义copyProperties,实现不同对象的相同属性(包含子对象)赋值相关的知识,希望对你有一定的参考价值。

Java自定义Copy属性

工具类中自带的BeanUtil.copyProperties只能赋值基本类型的属性。对于复杂类型:如List、Map仍然需要手动赋值。于是乎,自己定义了一个copyProperties工具方法,实现对复杂类型的的赋值。用于实体类与VO对象之间的复制。

实现共定义了四个方法,一个常用类(generalType)集合。除常用类、集合、列表以及数组之外,认为其他类型是自定义类型。

  1. 方法:copyProperties,公共,用于两个对象之间相同属性赋值,对象类型可同可不同;

  2. 方法:copyCollection,私有,不公开,用于集合之间赋值;

  3. 方法: copyList,公共,用于A泛型列表(List)转换为B泛型列表,默认为ArrayList类型。如果源对象字段属性和目标对象字段类型为不同的List,赋值类型以目标属性为主,如果目标属性为List类型,则字段属性类型以源对象字段属性类型为主

  4. 方法:copySet:公共,用于A泛型Set集合转换为B泛型集合,默认为HashSet类型。 如果源对象字段属性和目标对象字段类型为不同的Set,赋值类型以目标属性为主,如果目标属性为Set类型,则字段属性类型以源对象字段属性类型为主

  5. 方法:getField,私有,获取该类及其父类下指定的属性字段。

  6. 方法:getAllFields,私有,获取该类及其父类下所有的属性字段。
    代码:

  7. BeanUtil

import java.lang.reflect.*;
import java.util.*;

public final class BeanUtil 
    /**
     * 定义所有的基本类型及包装类型,或者是String、Date
     */
    private static final List<String> generalType = new ArrayList<String>() 
        add(Integer.class.getName());
        add(Double.class.getName());
        add(Float.class.getName());
        add(Long.class.getName());
        add(Short.class.getName());
        add(Byte.class.getName());
        add(Boolean.class.getName());
        add(Character.class.getName());
        add(String.class.getName());
        add(Date.class.getName());
        add("int");
        add("double");
        add("float");
        add("long");
        add("short");
        add("byte");
        add("boolean");
        add("char");
    ;

    /**
     * 获取本类及其父类的指定的属性
     *
     * @param clazz 当前类对象
     * @return 字段
     */
    private static final Field getField(Class<?> clazz, String name) 
        while (clazz != null) 
            try 
                return clazz.getDeclaredField(name);
             catch (Exception e) 
                clazz = clazz.getSuperclass();
            
        
        return null;
    

    /**
     * 获取本类及其父类的属性
     *
     * @param clazz 当前类对象
     * @return 字段数组
     */
    private static final Field[] getAllFields(Class<?> clazz) 
        List<Field> fieldList = new ArrayList<>();
        while (clazz != null) 
            fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields())));
            clazz = clazz.getSuperclass();
        
        Field[] fields = new Field[fieldList.size()];
        return fieldList.toArray(fields);
    
    /**
     * 用于两个对象属性名相同之间的复制
     *
     * 支持字段类型:
     *      1. 所有基本类型 ;
     *      2. String;
     *      3. Date;
     *      4. List(允许源对象、目标对象类型不一致,以目标对象为主。抽象类默认为ArrayList);
     *      5. Set(允许源对象、目标对象类型不一致,以目标对象为主。抽象类默认为HashSet);
     *      6. Map<S,T> 其中S,T为基本类型、String、Date、自定义类型;
     *      7. 自定义对象,包含自己本身,可以实现无限层级复制字段值(最重要的功能);
     *      8. 支持继承父类,父类属性也可赋值
     *      9. 其他未知
     *
     * @param source 原资源对象
     * @param target 目标资源对象
     * @param <T>    目标类型
     * @param <S>    原资源类型
     * @return
     */
    public static final <T, S> T copyProperties(S source, T target) 
        try 

            Class<?> sClass = source.getClass();
            Class<?> tClass = target.getClass();
            // 如果source为基本类型,直接复制
            if (generalType.contains(sClass.getName())) 
                target = (T) source;
                return target;
            
            // 获取所有字段及父类字段
            Field[] fields = getAllFields(sClass);
            for (int i = 0; i < fields.length; i++) 
                Field sField = fields[i];
                sField.setAccessible(true);
                // 获取字段Class
                Class<?> sFieldClass = sField.getType();
                // 获取字段名
                String name = sField.getName();
                // 获取字段值
                Object value = sField.get(source);
                if (value == null) 
                    // 值为空,跳过
                    continue;
                
                // 获取目标类字段
                Field tField;
                try 
                    // 尝试获取属性字段,获取不到不复制
                    tField = getField(tClass, name);
                    tField.setAccessible(true);
                 catch (Exception e) 
                    continue;
                
                Class<?> tFieldClass = tField.getType();
                if (generalType.contains(sFieldClass.getName())) 
                    // 基本类型 + String + Date,直接复制值
                    /**
                     * 判断源字段属性的类型和目标字段属性类型一致,进行赋值,否则跳过
                     */
                    if (tFieldClass.getName().equals(sFieldClass.getName())) 
                        tField.set(target, value);
                    
                 else if (tFieldClass.isArray()) 
                    // 数组类型
                    // 获取数组的Class
                    Class<?> componentType = tFieldClass.getComponentType();
                    if (generalType.contains(componentType.getName())) 
                        // 如果为通用类型数组,进行复制
                        tField.set(target, value);
                     else 
                        // 复杂类型使用该方法复制
                        Object[] arr = (Object[]) value;
                        Object targetArr = Array.newInstance(componentType, arr.length);
                        for (int j = 0; j < arr.length; j++) 
                            Object itemSource = arr[j];
                            // 定义目标对象
                            Object itemTarget = componentType.newInstance();
                            // 复制对象
                            copyProperties(itemSource, itemTarget);
                            // 添加到对应数组
                            Array.set(targetArr, j, itemTarget);
                        
                        // 复制属性
                        tField.set(target, targetArr);
                    
                 else if (List.class.isAssignableFrom(tFieldClass)) 
                    // 列表类型
                    // 强转列表值
                    List listValue = (List) value;
                    // 获取列表中的泛型
                    Type genericType = tField.getGenericType();
                    // 获取类型Class
                    if (genericType instanceof ParameterizedType) 
                        ParameterizedType typeCls = (ParameterizedType) genericType;
                        // 列表中的泛型类型
                        Class subGenericClass = (Class<?>) typeCls.getActualTypeArguments()[0];
                        // 最终属性值,默认转成了ArrayList
                        List targetList = copyList(listValue, subGenericClass);
                        // 复制属性值
                        // 如果为抽象类,或者目标类型与源类型相同,直接复制
                        if (tFieldClass == List.class || tFieldClass == targetList.getClass()) 
                            // 默认为ArrayList
                            tField.set(target, targetList);
                         else 
                            // 其他List
                            List otherList = (List) tFieldClass.newInstance();
                            // 转换目标类型列表
                            targetList.stream().forEach(item -> otherList.add(item));
                            // 复制
                            tField.set(target, otherList);
                        
                    
                 else if (Set.class.isAssignableFrom(tFieldClass)) 
                    // 列表类型
                    // 强转列表值
                    Set listValue = (Set) value;
                    // 获取列表中的泛型
                    Type genericType = tField.getGenericType();
                    // 获取类型Class
                    if (genericType instanceof ParameterizedType) 
                        ParameterizedType typeCls = (ParameterizedType) genericType;
                        // 列表中的泛型类型
                        Class subGenericClass = (Class<?>) typeCls.getActualTypeArguments()[0];
                        // 最终属性值,默认转成了ArrayList
                        Set targetSet = copySet(listValue, subGenericClass);
                        // 复制属性值
                        // 如果为抽象类,或者目标类型与源类型相同,直接复制
                        if (tFieldClass == Set.class || tFieldClass == targetSet.getClass()) 
                            // 默认为ArrayList
                            tField.set(target, targetSet);
                         else 
                            // 其他List
                            Set otherSet = (Set) tFieldClass.newInstance();
                            // 转换目标类型列表
                            targetSet.stream().forEach(item -> otherSet.add(item));
                            // 复制
                            tField.set(target, otherSet);
                        
                    
                 else if (Map.class.isAssignableFrom(tFieldClass)) 
                    // 集合类型
                    // 强转集合
                    Map mapValue = (Map) value;
                    // key值集合
                    List keys = new ArrayList(mapValue.keySet());
                    // value值集合
                    List values = new ArrayList(mapValue.values());
                    // 获取列表中的泛型
                    Type genericType = tField.getGenericType();
                    // 获取类型Class
                    if (genericType instanceof ParameterizedType) 
                        ParameterizedType typeCls = (ParameterizedType) genericType;
                        // 获取key的类型
                        Class keyClass = (Class) typeCls.getActualTypeArguments()[0];
                        // 获取value的类型
                        Class valueClass = (Class) typeCls.getActualTypeArguments()[1];
                        // 转换keys集合
                        List targetKeysList = copyList(keys, keyClass);
                        // 转换values集合
                        List targetValuesList = copyList(values, valueClass);
                        // 实例化map对象
                        Map targetValue;
                        try 
                            // 属性可能使用具体类
                            targetValue = (Map) tFieldClass.newInstance();
                         catch (Exception e) 
                            // 如果使用抽象类,则使用value值的类型
                            targetValue = (Map) value.getClass().newInstance();
                        
                        // 转换目标Map集合
                        for (int j = 0; j < targetKeysList.size(); j++) 
                            Object targetMapKey = targetKeysList.get(j);
                            Object targetMapValue = targetValuesList.get(j);
                            targetValue.put(targetMapKey, targetMapValue);
                        
                        // 复制属性
                        tField.set(target, targetValue);
                    
                 else 
                    // 自定义对象类型
                    // 定义目标对象
                    Object targetValue = tFieldClass.newInstance();
                    // 复制子属性值
                    copyProperties(value, targetValue);
                    // 复制属性值
                    tField.set(target, targetValue);
                
            
            return target;
         catch (Exception e) 
            e.printStackTrace();
        
        return null;
    

    /**
     * 复制集合
     * @param source
     * @param target
     * @param targetClass
     * @param <T>
     * @param <S>
     */
    private static final <T, S> void copyCollection(Collection<S> source, Collection<T> target, Class<T> targetClass) 
        source.stream().forEach(item -> 
            try 
                T t;
                // 如果是基本类型
                if (generalType.contains(targetClass.getName())) 
                    target.add((T) item);
                 else 
                    t = targetClass.newInstance();
                    copyProperties(item, t);
                    target.add(t);
                
             catch (Exception e) 
                e.printStackTrace();
            
        );
    

    /**
     * 实体类与VO对象列表的copy
     * 转换后以源列表为主,默认为ArrayList
     *
     * @param source      数据源列表
     * @param targetClass 目标列表中对象类Class
     * @param <T>         目标列表类型
     * @param <S>         数据源列表类型
     * @return
     */
    public static final <T, S> List<T> copyList(List<S> source, Class<T> targetClass) 
        List<T> target;
        try 
            if (source.getClass() == List.class || source.getClass() == ArrayList.class) 
                target = new ArrayList<>以上是关于Java自定义copyProperties,实现不同对象的相同属性(包含子对象)赋值的主要内容,如果未能解决你的问题,请参考以下文章

Java自定义copyProperties,实现不同对象的相同属性(包含子对象)赋值

Java反射小练之手写BeanUtils的copyProperties(Upgrade)

复制属性的Bean时只拷贝非null属性(跳过null属性)

java中 BeanUtils.copyProperties的用法

java BeanUtils.copyProperties 问题

BeanUtils.copyProperties无法拷贝问题