Java对象拷贝时根据枚举自动转换属性值

问题描述:在copyProperties(A,B)拷贝对象时,需要将A.type(Integer)拷贝到B.type(String),但是A.type是类型的数字编码,B.type是类型的描述,通过给B.type加一个注解指定枚举类,实现在拷贝属性的时候,自动转换类型编码到类型描述并赋值B.type;

JDK版本 >= 1.8 

枚举接口定义:

package com.example.visy.test;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * @author visy.wang
 * @date 2022/1/10 9:43
 */
public interface EnumConvert{
    //将映射关系缓存到Map中,便于快速查找
    Map<String,Object> map = new HashMap<>();

    //key映射道value
    default Object mapping(Object key){
        this.initMap();
        return map.get(String.valueOf(key));
    }

    default void initMap(){
        if(!map.isEmpty()){
            return;
        }
        for (EnumConvert item : getValues()) {
            map.put(String.valueOf(item.getKey()), item.getValue());
        }
    }

    Object getKey();
    // 返回的实际类型必须和目标属性的类型一致
    Object getValue();
    EnumConvert[] getValues();
}

 枚举定义:

package com.example.visy.test;

import lombok.AllArgsConstructor;
import lombok.Getter;

/**
 * 方向枚举 (必须实现EnumConvert接口)
 * @author visy.wang
 * @date 2022/1/10 9:42
 */
@Getter
@AllArgsConstructor
public enum DirectionEnum implements EnumConvert {
    TOP(1, "上"),
    DOWN(2, "下"),
    LEFT(3, "左"),
    RIGHT(4, "右");

    private final Integer code;
    private final String desc;

    @Override
    public Object getKey() {
        return code;
    }

    @Override
    public Object getValue() {
        //注意:必须和待转换的目标属性类型相同,否则不会生效
        return desc;
    }

    @Override
    public EnumConvert[] getValues() {
        return DirectionEnum.values();
    }
}

注解定义:

package com.example.visy.test;

import java.lang.annotation.*;
import java.lang.annotation.Target;

/**
 * 枚举转换注解
 * @author visy.wang
 * @date 2022/1/10 10:11
 */
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface EnumMapper {
    //指定枚举Class
    Class<? extends Enum<?>> value();
    //指定源对象中对应的属性别名(属性名相同时,无需指定alias)
    String alias() default "";
}

 拷贝工具定义:

package com.example.visy.test;

import java.lang.reflect.Field;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author visy.wang
 * @date 2022/1/10 11:28
 */
public class BeanUtils {
    private static final Map<String, List<Field>> TARGET_FIELD_MAP = new HashMap<>();
    private static final Map<String, Map<String,Field>> SOURCE_FIELD_MAP = new HashMap<>();

    public static void copyProperties(Object source, Object target) throws IllegalAccessException {
        List<Field> targetFields = getFields(target);
        if(Objects.isNull(targetFields) || targetFields.isEmpty()){
            return;
        }

        for(Field targetField: targetFields){
            Field sourceField = getField(source, targetField.getName());

            if(targetField.isAnnotationPresent(EnumMapper.class)){ //检查注解
                EnumMapper enumMapper = targetField.getAnnotation(EnumMapper.class);

                String aliasName = enumMapper.alias();
                if(Objects.nonNull(aliasName) && !aliasName.isEmpty()){
                    //指定了别名的情况,优先使用别名
                    sourceField = getField(source, aliasName);
                }

                if(Objects.isNull(sourceField)){
                    continue; //源属性为空->不拷贝
                }

                if(enumMapper.value().isEnum()){//注解中指定的Class必须是枚举
                    Enum<?> mapper = enumMapper.value().getEnumConstants()[0]; //随便取一个枚举值
                    if(mapper instanceof EnumConvert){//枚举必须实现了EnumConvert接口
                        Object oldValue = sourceField.get(source);
                        //转换后拷贝
                        Object newValue = ((EnumConvert) mapper).mapping(oldValue);
                        if(Objects.nonNull(newValue) && targetField.getType().equals(newValue.getClass())){
                            targetField.set(target, newValue);
                            continue;
                        }
                    }
                }
            }

            if(Objects.isNull(sourceField)){
                continue; //源属性为空->不拷贝
            }

            if(!targetField.getType().equals(sourceField.getType())){
                continue; //两个属性类型不一致->不拷贝
            }

            //拷贝
            targetField.set(target, sourceField.get(source));
        }

    }

    private static List<Field> getFields(Object obj){
        Class<?> clazz = obj.getClass();
        String key = clazz.getName();
        List<Field> fields = TARGET_FIELD_MAP.get(key);
        if(Objects.isNull(fields)){
            Field[] fieldArray = clazz.getDeclaredFields();
            Field.setAccessible(fieldArray, true);
            fields = Arrays.stream(fieldArray).collect(Collectors.toList());
            TARGET_FIELD_MAP.put(key, fields);
        }
        return fields;
    }

    private static Field getField(Object obj, String fieldName){
        Class<?> clazz = obj.getClass();
        String key = clazz.getName();
        Map<String,Field> map = SOURCE_FIELD_MAP.get(key);
        if(Objects.isNull(map)){
            Field[] fieldArray = clazz.getDeclaredFields();
            Field.setAccessible(fieldArray, true);
            map = Arrays.stream(fieldArray).collect(Collectors.toMap(Field::getName, Function.identity()));
            SOURCE_FIELD_MAP.put(key, map);
        }
        return map.get(fieldName);
    }
}

源对象:

package com.example.visy.test;

import lombok.Data;

/**
 * 源对象
 * @author visy.wang
 * @date 2022/1/10 11:26
 */
@Data
public class Source {
    private Integer top = 1;
    private Integer down = 2;
    private Integer left = 3;
    private Integer right = 4;
    private Integer center = 5;

    private String name = "张三";
}

 目标对象:

package com.example.visy.test;

import lombok.Data;

/**
 * 目标对象
 * @author visy.wang
 * @date 2022/1/10 10:11
 */
@Data
public class Target {
    //和Source.top同名
    //Long和枚举的getValue()的实际类型(String)不匹配
    //Long和Source.top类型(Integer)不匹配
    //结果为:null
    @EnumMapper(DirectionEnum.class)
    private Long top;

    //和Source.down同名
    //Integer和枚举的getValue()的实际类型(String)不匹配
    //Integer和Source.down类型(Integer)匹配
    //结果为:2
    @EnumMapper(DirectionEnum.class)
    private Integer down;

    //和Source.left同名
    //String和枚举的getValue()的实际类型(String)匹配
    //结果为:"左"
    @EnumMapper(DirectionEnum.class)
    private String left;

    //和Source.right不同名,但指定了别名"right"
    //String和枚举的getValue()的实际类型(String)匹配
    //结果为:"右"
    @EnumMapper(value = DirectionEnum.class, alias = "right")
    private String rightStr;

    //无需转换的属性只要同名且类型一致就可正常拷贝
    private String name;
}

测试类:

package com.example.visy.test;

/**
 * @author visy.wang
 * @date 2022/1/10 10:47
 */
public class Test {
    public static void main(String[] args) throws Exception{
        Source source = new Source();
        Target target = new Target();

        BeanUtils.copyProperties(source, target);

        System.out.println(target);
    }
}

打印结果:

 Target(top=null, down=2, left=左, rightStr=右, name=张三)

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java 基础核心总结》 Java 概述 什么是 Java2 Java 的特点Java 开发环境 JDK JRE Java 开发环境配 Java 基本语法 数据类型基础语法运算符 Java 执行控制流程条件语句 if 条件语句 if...else 条件语句if...else if 多分支语句switch 多分支语句 循环语句 while 循环语句do...while 循环for 循环语句 跳转语句 break 语句 continue 语句面向对象 类也是-种对象对象的创建 属性和方法 构造方法 方法重载 方法的重写 初始化 类的初始化 成员初始化 构造器初始化初始化顺序 数组初始化 对象的销毁 对象作用域 this 和 super 访问控制权限继承 多态组合代理 向上转型static final 接口和抽象类接口 抽象类异常 认 识 Exception 什么是 Throwable 常见的 Exception 与 Exception 有关的 Java 关键字 throws 和 throw try 、finally 、catch 什么是 Error 内部类 创建内部类集合 Iterable 接口顶层接口 ArrayList Vector LinkedList 类Stack HashSet TreeSet LinkedHashSet 类 PriorityQueue HashMap TreeMap 类 LinkedHashMap 类 Hashtable 类IdentityHashMap 类WeakHashMap 类 Collections 类集合实现类特征图 泛形 泛型的使用 用泛型表示类 用泛型表示接口泛型方法 泛型通配符 反射 Class 类Field 类Method 类ClassLoader 类 枚举 枚举特性 枚举和普通类-样枚举神秘之处 枚举类 I/O File 类 基础 IO 类和相关方法InputStream OutputStream Reader 类Writer 类 InputStream 及其子类 OutputStream 及其子类Reader 及其子类Writer 及其子类 注解 关于 null 的几种处理方式大小写敏感 null 是任何引用类型的初始 null 只是-种特殊的使用 Null-Safe 方法null 判断 关于思维导图 Java.IO Java.lang Java.math Java.net Java 基础核心总结 V2.0 IO 传统的 BIO BIO NIO 和 AIO 的区别什么是流 流的分类 节点流和处理流 Java IO 的核心类 File Java IO 流对象 字节流对象InputStream OutputStream 字符流对象Reader Writer 字节流与字符流的转换新潮的 NIO 缓冲区(Buffer)通道(Channel) 示例:文件拷贝案例 BIO 和 NIO 拷贝文件的区别操作系统的零拷贝 选择器(Selectors) 选择键(SelectionKey) 示例:简易的客户端服务器通信 集合 集合框架总览 -、Iterator Iterable ListIterator 二、Map 和 Collection 接口Map 集合体系详解 HashMap LinkedHashMap TreeMap WeakHashMap Hashtable Collection 集合体系详解 Set 接口 AbstractSet 抽象类SortedSet 接口HashSet LinkedHashSet TreeSet List 接口 AbstractList 和 AbstractSequentialList Vector Stack ArrayList LinkedList Queue接口Deque 接口 AbstractQueue 抽象类LinkedList ArrayDeque PriorityQueue 反射的思想及作用 反射的基本使用 获取类的 Class 对象构造类的实例化对象获取-个类的所有信息 获取类中的变量(Field) 获取类中的方法(Method) 获取类的构造器(Constructor) 获取注解 通过反射调用方法反射的应用场景 Spring 的 IOC 容器反射 + 抽象工厂模式 JDBC 加载数据库驱动类反射的优势及缺陷 增加程序的灵活性破坏类的封装性 性能损耗 代理模式 静态代理与动态代理常见的动态代理实现JDK Proxy CGLIB JDK Proxy 和 CGLIB 的对比动态代理的实际应用 Spring AOP 变量 变量汇总实例变量 实例变量的特点全局变量 静态变量 静态变量的特点类变量 局部变量

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值