侧边栏壁纸
博主头像
落叶人生博主等级

走进秋风,寻找秋天的落叶

  • 累计撰写 130562 篇文章
  • 累计创建 28 个标签
  • 累计收到 9 条评论
标签搜索

目 录CONTENT

文章目录

简单实用的对象转换复制工具

2023-12-18 星期一 / 0 评论 / 0 点赞 / 22 阅读 / 7818 字

一、概述 工作中经常会遇到这样的场景,需要把对象A中的变量复制到对象B中,这是一个枯燥又没有技术含量的工作,最繁杂枯燥的方法是先调用A对象的get方法将A中待复制的变量取出然后再调用B对象的set方法

一、概述

    工作中经常会遇到这样的场景,需要把对象A中的变量复制到对象B中,这是一个枯燥又没有技术含量的工作,最繁杂枯燥的方法是先调用A对象的get方法将A中待复制的变量取出然后再调用B对象的set方法将对应的变量set到B对象中得到结果。后来有了BeanUtils提供的BeanUtils.copyProperties()方法可以简单快速的复制对象,但是新的问题又来了:如果对象A中的变量名和对象B中的变量名不同怎么办?就好比我们需要将对象A中的Integer a复制到对象B中的Integer b中,这时BeanUtils就无法派上用场了。所以为了使对象的复制更具普适性,我提供了一个对象转换复制的工具。

二、条件

    我们先将概述中的需求用代码体现出来,由于是测试类,所以写的就比较随意了。

    首先我们随便定一个类Temp:

@Dataclass Temp {    String s;}

    然后我们有一个待转换类Source:

@Dataclass Source {    String a;    Integer b;    Temp sourceTemp;    String[] d;    String[] sourceE;    String[] sourceF;}

    最后我们要有一个结果类Target:

@Dataclass Target {    Integer a;    String b;    Temp targetTemp;    String[] d;    String[] targetE;}

    可以看出Source类和Target类里有的变量名相同有的变量名不同,我们想要做的就是要忽略掉变量sourceF,不复制d,并且将sourceTemp复制到targetTemp,sourceE复制到targetE中,然后得到Target类。

三、实现

    看到这个场景,我第一反应就是注解,通过在Source上添加注解指定对应的Target,所以我第一步就是创建一个TransTarget注解来指定目标对象的变量名。

@Target({ElementType.FIELD})@Retention(RetentionPolicy.RUNTIME)@Inherited@Documentedpublic @interface TransTarget {    String value();   //目标对象变量名}

    有了思路之后接下来自然就是如何使用这个注解的问题。我的想法是创建一个方法,将Source对象和Target的class作为入参传入,使用反射拿到Source的Field列表,然后遍历列表检查是否有TransTarget注解标注,如果有,我就通过反射去调用Target中对应的set方法;如果没有,我就通过翻着调用Target中和Source相同变量名的变量的set方法。以下是具体实现。

    public static <K, V> V transBean(K k, Class<V> vClass) throws IllegalAccessException, InvocationTargetException {        Class<?> kClass = k.getClass();        List<Field> fieldList = Arrays.asList(kClass.getDeclaredFields());        //循环待转换变量,通过反射一一转换得到结果。        V v = null;        try {            v = vClass.getDeclaredConstructor().newInstance();        } catch (InstantiationException e) {            throw new ObjectTransException("无法实例化抽象类或接口。", e);        } catch (NoSuchMethodException e) {            throw new ObjectTransException(vClass.getName() + "无默认构造方法。", e);        }        for (Field field : fieldList) {            String name = field.getName();            TransTarget transTarget = field.getAnnotation(TransTarget.class);            String s = transTarget != null ? transTarget.value() : name;            Field targetField = null;            try {                targetField = vClass.getDeclaredField(s);            } catch (NoSuchFieldException e) {                continue;            }            if (field.getType().equals(targetField.getType())) {                String sourceMethodName = "get" + name.substring(0, 1).toUpperCase() + name.substring(1);                String targetMethodName = "set" + s.substring(0, 1).toUpperCase() + s.substring(1);                Method sourceMethod = null;                Method targetMethod = null;                try {                    sourceMethod = kClass.getDeclaredMethod(sourceMethodName);                    targetMethod = vClass.getDeclaredMethod(targetMethodName, field.getType());                } catch (NoSuchMethodException e) {                    throw new ObjectTransException("无法获取到对应方法。", e);                }                Object invoke = sourceMethod.invoke(k);                targetMethod.invoke(v, invoke);            }        }        return v;    }

    这里我只处理了部分异常,其他的就由调用者自行处理好了。这样就基本实现了我们需要的功能,接下来我们还需要忽略指定的变量d。这里我采取的方法还是使用注解,创建一个注解IgnoreFields,里面用一个字符串来标记哪些变量不需要复制。

@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Inherited@Documentedpublic @interface IgnoreFields {    String[] value() default {};}

    然后在transBean方法中对应的地方加上判断语句

        if (kClass.isAnnotationPresent(IgnoreFields.class)) {            IgnoreFields ignoreFields = kClass.getAnnotation(IgnoreFields.class);            List<String> ignoreFiledList = Arrays.asList(ignoreFields.value());            if (CollectionUtils.isNotEmpty(ignoreFiledList)) {                fieldList = fieldList.stream()                        .filter(field -> !ignoreFiledList.contains(field.getName()))                        .collect(Collectors.toList());            }        }

    转换工具就大功告成了,下面我们需要改造一下Source类。

@Data@IgnoreFields("d")class Source {    String a;    Integer b;    @TransTarget("targetTemp")    Temp sourceTemp;    String[] d;    @TransTarget("targetE")    String[] sourceE;    String[] sourceF;}

    最后我们来测试一下效果如何:

    public static void main(String[] args) throws InvocationTargetException, NoSuchFieldException, IllegalAccessException {        Temp temp = new Temp();        temp.setS("testTemp");        Source source = new Source();        source.setA("a");        source.setB(1);        source.setSourceTemp(temp);        String[] test1s = {"d", "d"};        String[] test2s = {"e", "e"};        String[] test3s = {"f", "f"};        source.setD(test1s);        source.setSourceE(test2s);        source.setSourceF(test3s);        Target target = ObjectTransUtil.transBean(source, Target.class);        System.out.println(target);    }

    最后得到的结果是:

    可见Source中对应的字段都复制到了Target中,字段名不同的也成功复制,想要忽略的也都忽略掉了,并且使用也很方便,大功告成。

    当然,这些代码还有很大的优化空间,希望看到这篇文章的朋友可以提出宝贵的意见,大家一起交流提升。

------------------------------------------------------------------------

欢迎关注我的个人公众号,推送最新文章

广告 广告

评论区