工具类-JAVA反射复制不同类属性

有两个java对象:想将类Source的属性值复制给类Target,根据相同属性名赋值。

通过反射复制相似对象的属性。两个对象类似,但有部分属性名不同,但想要将相同属性名的值传递过去。


场景:

有两个java对象:想将Source的属性名相同的属性复制给Target。

@Data
class Source{
    String name;
    Source ceShi;
    String aDouble;
}
@Data
class Target{
    String name;
    Source ceShi;
    Double aD;
}

最简单粗暴的办法是直接,写一个方法,挨个把属性set赋值进去。

问题是下次再遇到类似场景,还要再重写一个方法,不具有普适性,很麻烦。

有没有什么一劳永逸的方法?


实现思路:

目标是:将source对象复制给target对象,虽然两个对象的父类都是Object,但当调用方法时传入的两个对象未知类型。

 /**
     * 将source的属性赋值给target相应属性<br/>
     * 注意:<br/>
     *  1.source和target的属性名相同时,属性类型也必须相同<br/>
     *  2.source和target的属性类型必须全部为基础属性的包装类或其他类,不能使用类似<T>这种泛型。
     *      如:只能写为Boolean,不能为boolean<br/>
     *  3.source和target所有属性必须有set方法,其属性类型和其get/set方法参数类型必须相同。<br/>
     *  4.浅复制,若source里的某属性为一对象,相当于直接让target的属性等于source里的属性对象<br/>
     *
     * @param source
     * @param target
     */
    public static void injectSetField(Object source,Object target) throws Exception {
        //得到class
        Class<?> sourceClass = source.getClass();
        Class<?> targetClass = target.getClass();
        //得到obj所有属性
        Field[] objFields = sourceClass.getDeclaredFields();
        Field[] tarFields = targetClass.getDeclaredFields();
        //子方法:数组转map,key为属性名,val为Field对象
        Map<String, Field> tarMap = fieldsToMap(tarFields);
        //遍历objFields
        for (int i = 0; i < objFields.length; i++) {
            //属性名
            //得到属性
            Field field = objFields[i];
            //打开私有访问
            field.setAccessible(true);
            //获取属性
            String name = field.getName();
            if (tarMap.containsKey(name)){
                //存在该属性,可以进行复制
                //获取属性值
                Object value = field.get(source);
                //子方法:对target对象的属性name赋值value
                injectSetField_1(target, name, value);
            }
        }
    }

上面方法逻辑是:通过反射获取source和target两个对象的属性数组Field[],比较两个数组,有相同属性的,就调用给对象反射赋值的子方法。

就是下一个方法。

  /**
     * 给对象model 的属性attrName 赋值为 attrVal。
     * 浅复制,属性为对象不为基础类型,则直接赋给对象。
     * 注意:model不能使用<T>这种泛型,且其属性的类型必须全部为包装类,即不能写boolean,必须为Boolean,
     * 其model的get/set方法里参数也必须为Boolean这种
     * @param model
     * @param attrName
     * @param attrVal
     * @return
     * @throws Exception
     */
    public static void injectSetField_1(Object model, String attrName,Object attrVal) throws Exception {
        Field[] field = model.getClass().getDeclaredFields(); // 获取实体类的所有属性,返回Field数组
        //用于转换attrVal
        Object val;

        for (int j = 0; j < field.length; j++) { // 遍历所有属性
            String name = field[j].getName(); // 获取属性的名字
            Class<?> type = field[j].getType();

            //找到了该属性,进行赋值操作
            if (name.equals(attrName)) {
                //attrVal转换为其类型,若类型不一致,为null
                val = convertByType(attrVal, type);
                name = name.substring(0, 1).toUpperCase() + name.substring(1); // 将属性的首字符大写,方便构造get,set方法
                Method m = null;
                if (attrVal != null){
                    if (val != null)
                    {
                        m = model.getClass().getMethod("set" + name, val.getClass());
                        m.invoke(model, val);
                    }else {
                        m = model.getClass().getMethod("set" + name, attrVal.getClass());
                        m.invoke(model, attrVal);
                    }
                }
            }
        }
    }

上面这个方法是传入3个参数,对象,属性名,属性值。给对象的属性赋值。

逻辑是通过反射调用其对象的set方法,来进行属性的赋值。

下面是两个工具方法,都是上面方法中需要使用的:

​ 1.将field[]数组转map

​ 2.Object对象转成指定的类型对象

 /**
     * field[]数组转map
     * @param fields  属性数组
     * @return map
     */
    public  static  Map<String, Field> fieldsToMap(Field[] fields){
        HashMap<String, Field> hashMap = new HashMap<>();
        for (Field field : fields) {
            //打开私有访问
            field.setAccessible(true);
            String name = field.getName();
            hashMap.put(name, field);
        }
        return hashMap;
    }



    /**
     * Object转成指定的类型type
     * @param obj
     * @param type
     * @param <T>
     * @return
     *
     */
    public static<T> T convertByType(Object obj, Class<T> type) {
        if (obj != null && ItsStringUtil.isNotEmpty(obj.toString())) {
            if (type.equals(Integer.class)||type.equals(int.class)) {
                return (T)new Integer(obj.toString());
            } else if (type.equals(Long.class)||type.equals(long.class)) {
                return (T)new Long(obj.toString());
            } else if (type.equals(Boolean.class)||type.equals(boolean.class)) {
                return (T) new Boolean(obj.toString());
            } else if (type.equals(Short.class)||type.equals(short.class)) {
                return (T) new Short(obj.toString());
            } else if (type.equals(Float.class)||type.equals(float.class)) {
                return (T) new Float(obj.toString());
            } else if (type.equals(Double.class)||type.equals(double.class)) {
                return (T) new Double(obj.toString());
            } else if (type.equals(Byte.class)||type.equals(byte.class)) {
                return (T) new Byte(obj.toString());
            } else if (type.equals(Character.class)||type.equals(char.class)) {
                return (T)new Character(obj.toString().charAt(0));
            } else if (type.equals(String.class)) {
                return (T) obj;
            } else if (type.equals(BigDecimal.class)) {
                return (T) new BigDecimal(obj.toString());
            } else if (type.equals(LocalDateTime.class)) {
                return (T) LocalDateTime.parse(obj.toString());
            } else if (type.equals(Date.class)) {
                return (T)((Date)obj);
            }else{
                return null;
            }
        } else {
            if (type.equals(int.class)) {
                return (T)new Integer(0);
            } else if (type.equals(long.class)) {
                return (T)new Long(0L);
            } else if (type.equals(boolean.class)) {
                return (T)new Boolean(false);
            } else if (type.equals(short.class)) {
                return (T)new Short("0");
            } else if (type.equals(float.class)) {
                return (T) new Float(0.0);
            } else if (type.equals(double.class)) {
                return (T) new Double(0.0);
            } else if (type.equals(byte.class)) {
                return (T) new Byte("0");
            } else if (type.equals(char.class)) {
                return (T) new Character('\u0000');
            }else {
                return null;
            }
        }
    }


main测试

    public static void main(String[] args) {
        Source source = new Source();
        source.setADouble("123456");
        source.setName("name1");
        source.setSource(new Source());
        Target two =new Target();
        System.out.println(source);
        System.out.println(two);
        System.out.println("-----------");
        try {
            injectSetField(source,two);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(source);
        System.out.println(two);
    }
}

测试结果:

在这里插入图片描述


关注后查看更多,码不听题

在这里插入图片描述


版权声明:本文为qq_43220949原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。