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

2023-10-29

问题描述:在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=张三)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java对象拷贝时根据枚举自动转换属性值 的相关文章

  • Grails 3.x bootRun 失败

    我正在尝试在 grails 3 1 11 中运行一个项目 但出现错误 失败 构建失败并出现异常 什么地方出了错 任务 bootRun 执行失败 进程 命令 C Program Files Java jdk1 8 0 111 bin java
  • 在 Java 中连接和使用 Cassandra

    我已经阅读了一些关于 Cassandra 是什么以及它可以做什么的教程 但我的问题是如何在 Java 中与 Cassandra 交互 教程会很好 如果可能的话 有人可以告诉我是否应该使用 Thrift 还是 Hector 哪一个更好以及为什
  • Java new Date() 打印

    刚刚学习 Java 我知道这可能听起来很愚蠢 但我不得不问 System out print new Date 我知道参数中的任何内容都会转换为字符串 最终值是 new Date 返回对 Date 对象的引用 那么它是如何打印这个的呢 Mo
  • 为什么 i++ 不是原子的?

    Why is i Java 中不是原子的 为了更深入地了解 Java 我尝试计算线程中循环的执行频率 所以我用了一个 private static int total 0 在主课中 我有两个线程 主题 1 打印System out prin
  • 在 HTTPResponse Android 中跟踪重定向

    我需要遵循 HTTPost 给我的重定向 当我发出 HTTP post 并尝试读取响应时 我得到重定向页面 html 我怎样才能解决这个问题 代码 public void parseDoc final HttpParams params n
  • INSERT..RETURNING 在 JOOQ 中不起作用

    我有一个 MariaDB 数据库 我正在尝试在表中插入一行users 它有一个生成的id我想在插入后得到它 我见过this http www jooq org doc 3 8 manual sql building sql statemen
  • 列出jshell中所有活动的方法

    是否有任何命令可以打印当前 jshell 会话中所有新创建的方法 类似的东西 list但仅适用于方法 您正在寻找命令 methods all 它会打印所有方法 包括启动 JShell 时添加的方法 以及失败 被覆盖或删除的方法 对于您声明的
  • 反射找不到对象子类型

    我试图通过使用反射来获取包中的所有类 当我使用具体类的代码 本例中为 A 时 它可以工作并打印子类信息 B 扩展 A 因此它打印 B 信息 但是当我将它与对象类一起使用时 它不起作用 我该如何修复它 这段代码的工作原理 Reflection
  • JavaMail 只获取新邮件

    我想知道是否有一种方法可以在javamail中只获取新消息 例如 在初始加载时 获取收件箱中的所有消息并存储它们 然后 每当应用程序再次加载时 仅获取新消息 而不是再次重新加载它们 javamail 可以做到这一点吗 它是如何工作的 一些背
  • 斯坦福 NLP - 处理文件列表时 OpenIE 内存不足

    我正在尝试使用斯坦福 CoreNLP 中的 OpenIE 工具从多个文件中提取信息 当多个文件 而不是一个 传递到输入时 它会给出内存不足错误 All files have been queued awaiting termination
  • 如何在PreferenceActivity中添加工具栏

    我已经使用首选项创建了应用程序设置 但我注意到 我的 PreferenceActivity 中没有工具栏 如何将工具栏添加到我的 PreferenceActivity 中 My code 我的 pref xml
  • Java TestNG 与跨多个测试的数据驱动测试

    我正在电子商务平台中测试一系列商店 每个商店都有一系列属性 我正在考虑对其进行自动化测试 是否有可能有一个数据提供者在整个测试套件中提供数据 而不仅仅是 TestNG 中的测试 我尝试不使用 testNG xml 文件作为机制 因为这些属性
  • Java列表的线程安全

    我有一个列表 它将在线程安全上下文或非线程安全上下文中使用 究竟会是哪一个 无法提前确定 在这种特殊情况下 每当列表进入非线程安全上下文时 我都会使用它来包装它 Collections synchronizedList 但如果不进入非线程安
  • 玩!框架:运行“h2-browser”可以运行,但网页不可用

    当我运行命令时activator h2 browser它会使用以下 url 打开浏览器 192 168 1 17 8082 但我得到 使用 Chrome 此网页无法使用 奇怪的是它以前确实有效 从那时起我唯一改变的是JAVA OPTS以启用
  • 获取 JVM 上所有引导类的列表?

    有一种方法叫做findBootstrapClass对于一个类加载器 如果它是引导的 则返回一个类 有没有办法找到类已经加载了 您可以尝试首先通过例如获取引导类加载器呼叫 ClassLoader bootstrapLoader ClassLo
  • 在 Maven 依赖项中指定 jar 和 test-jar 类型

    我有一个名为 commons 的项目 其中包含运行时和测试的常见内容 在主项目中 我添加了公共资源的依赖项
  • 使用 JMF 创建 RTP 流时出现问题

    我正处于一个项目的早期阶段 需要使用 RTP 广播DataStream创建自MediaLocation 我正在遵循一些示例代码 该代码目前在rptManager initalize localAddress 出现错误 无法打开本地数据端口
  • 当我从 Netbeans 创建 Derby 数据库时,它存储在哪里?

    当我从 netbeans 创建 Derby 数据库时 它存储在哪里 如何将它与项目的其余部分合并到一个文件夹中 右键单击Databases gt JavaDB in the Service查看并选择Properties This will
  • 如何修复 JNLP 应用程序中的“缺少代码库、权限和应用程序名称清单属性”?

    随着最近的 Java 更新 许多人都遇到了缺少 Java Web Start 应用程序的问题Codebase Permissions and Application name体现属性 尽管有资源可以帮助您完成此任务 但我找不到任何资源综合的
  • 使用 xpath 和 vtd-xml 以字符串形式获取元素的子节点和文本

    这是我的 XML 的一部分

随机推荐

  • 【C++】函数的设计与使用(三)重载函数、模板函数

    重载函数 参数列表不相同 可能是参数类型不相同 或者参数个数不相同 都不相同 也可以 的两个或多个函数 可以拥有相同的函数名称 编译器会把实参和每个重载函数的形参比对 找出哪个重载函数合适 所以每个重载函数的参数列表必须和其他的重载函数的不
  • ubuntu安装bochs,nasm

    1 ubuntu上安装bochs nasm 1 1 安装缘由 最近想自己做个操作系统玩一玩巩固巩固知识 工欲善其事 必先利其器 开发操作系统首先得搭建环境 编程语言上我选择C和汇编完成 开发环境是在我装的一个虚拟机ubuntu上 ubunt
  • 【满分】【华为OD机试真题2023 JAVA&JS】统计匹配的二元组个数

    华为OD机试真题 2023年度机试题库全覆盖 刷题指南点这里 统计匹配的二元组个数 知识点数组 时间限制 1s 空间限制 32MB 限定语言 不限 题目描述 给定两个数组A和B 若数组A的某个元素A i 与数组B中的某个元素B j 满足 A
  • NodeJS - Express使用

    文章目录 1 参数 1 1 获取URL中的动态参数 2 静态资源 2 1 挂载路径前缀 3 nodemon 4 1路由 4 1 路由的匹配过程 4 2 模块化路由 4 3 为路由模块添加前缀 5 中间件 5 1 全局生效的中间件 5 2 全
  • 功能强大,但因安全隐患被企业禁用的Python内置函数

    功能强大 但却因安全隐患被企业禁用的Python内置函数 eval 函数是Python的内置函数 功能非常强大 但是存在不小的安全隐患 有些企业或项目出于安全考虑 禁止使用eval 函数 会在一些安全相关的扫描校验中进行识别和拦截 杜绝使用
  • 【嵌入式】虚拟机未能将管道连接到虚拟机: 系统找不到指定的文件

    这两天虚拟机莫名奇妙的爆出这个错误 在升级win11过后解决嘞这个问题 但是win11确实不好用最后退回win10这个问题又出现了 这里记录一下我的解决办法 设置为管理员运行程序 然后遇到新的报错了 进入控制面板选择C 2015修复环境 到
  • React性能提升

    了解react如何提升性能将有助于我们更好的编写代码 个人认为react中很多的性能优化 其实都是围绕着react的核心diff算法来展开的 通过优化 减少diff算法中一些不必要的步骤 从而来提高性能 下面是我平时开发总结出来的一些经验
  • QT控件之(TableView)中设置为不可编辑状态

    加入以下一句代码 ui gt tableView gt setEditTriggers QAbstractItemView NoEditTriggers
  • 【H.264/AVC视频编解码技术详解】二十三、帧间预测编码(1):帧间预测编码的基本原理

    H 264 AVC视频编解码技术详解 视频教程已经在 CSDN学院 上线 视频中详述了H 264的背景 标准协议和实现 并通过一个实战工程的形式对H 264的标准进行解析和实现 欢迎观看 纸上得来终觉浅 绝知此事要躬行 只有自己按照标准文档
  • STM32初始化USART后只发送了一个0x00,而无法发送其他数据的解决方法

    GPIO InitTypeDef GPIO InitStructure USART InitTypeDef USART InitStructure RCC APB2PeriphClockCmd RCC APB2Periph GPIOB EN
  • Tornado入门教程

    Overview FriendFeed是一款使用 Python 编写的 相对简单的 非阻塞式 Web 服务器 其应用程序使用的 Web 框架看起来有些像 web py 或者 Google 的 webapp 不过为了能有效利用非阻塞式服务器环
  • 【数据结构与算法】顺序表精讲

    所属专栏 数据结构与算法 前期内容 绪论1 抽象数据类型 绪论2 时间复杂度与空间复杂度 与时间复杂度相关的OJ面试题 抽象数据类型的实现 目录 1 1线性表的定义和特点 1 2案例引入 eg 图书信息管理系统 1 3线性表的类型定义 基本
  • code-server安装使用

    code server是一款基于VSCode的在线编辑器 它提供了一个web话的在线IDE 服务端上运行code server 可以理解为一个在远程服务器上运行的VS Code 客户端就可以使用浏览器打开vscode vscode上的资源都
  • 软件工程实训day01-mybatis

    本次实训环境 jdk 1 8 编辑器 eclipse 数据库 mysql SQLyog 实训所得结果 使用mybatis进行简单增删改查操作 代码上传gitee 创建数据库 DROP TABLE IF EXISTS computers CR
  • 非管理员用户添加右键菜单(管理员也适用)

    目录 背景 解决办法 总结 把其他教程中的HKEY CLASSES ROOT替换为HKEY CURRENT USER SOFTWARE Classes即可 背景 网上大多数教程添加右键菜单的方法都需要管理员权限 公司电脑处于信息安全角度 大
  • Jenkins自动化打包上传到Fir报Can not found mapping or dysm file in的解决方案

    在踩了几个坑以后 Jenkins自动打包终于成功了 就想着自动上传到Fir 自动生成二维码 便于下载安装 在网上找到了一个Fir插件 安装成功以后再次打包发现报Can not found mapping or dysm file in 在网
  • 使用JDK开发WebService-客户端建立

    一 序言 上篇 我已经写了一个简单的WebService服务器 看起来貌似不错 但我们真正的目的是想通过编程的方式来访问服务器 而不是通过eclipse带的Web Service浏览器来访问 服务器我们是一行行代码敲出来的 客户端同样我们也
  • vue 创建一个登录界面

    vue创建一个登录界面 1 创建登录界面和主页 2 配置路由 3 配置main js 4 配置App vue 5 登录页面 6 主页面 用到的组件 参考链接 1 创建登录界面和主页 打开搭建好的vue项目 在components文件夹下面新
  • 电信光猫改桥接模式

    如果只是改桥接 可以试试下面这两个地址 http 192 168 1 1 bridge route gchhttp 192 168 1 1 8080 bridge route gch 转载于 https www cnblogs com De
  • Java对象拷贝时根据枚举自动转换属性值

    问题描述 在copyProperties A B 拷贝对象时 需要将A type Integer 拷贝到B type String 但是A type是类型的数字编码 B type是类型的描述 通过给B type加一个注解指定枚举类 实现在拷