2021-03-11 15:17:27 +08:00
|
|
|
|
# 我的公众号
|
|
|
|
|
|

|
|
|
|
|
|
|
2026-05-17 09:32:34 +08:00
|
|
|
|
# Java_ConstractUtil
|
|
|
|
|
|
|
|
|
|
|
|
通用的Java同类型对象对比工具类,用于比较两个同类型对象的属性差异,并返回变化的部分。
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 快速开始
|
|
|
|
|
|
|
|
|
|
|
|
### Maven依赖
|
|
|
|
|
|
|
|
|
|
|
|
```xml
|
|
|
|
|
|
<dependency>
|
|
|
|
|
|
<groupId>Java_ConstractUtil</groupId>
|
|
|
|
|
|
<artifactId>Java_ConstractUtil</artifactId>
|
|
|
|
|
|
<version>1.0.0</version>
|
|
|
|
|
|
</dependency>
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 基本使用
|
|
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
|
// 比较两个同类型对象
|
|
|
|
|
|
SimpleObj origin = new SimpleObj();
|
|
|
|
|
|
origin.setX(1);
|
|
|
|
|
|
origin.setY("hello");
|
|
|
|
|
|
|
|
|
|
|
|
SimpleObj target = new SimpleObj();
|
|
|
|
|
|
target.setX(2);
|
|
|
|
|
|
target.setY("hello");
|
|
|
|
|
|
|
|
|
|
|
|
ContrastObject<SimpleObj> result = ContrastUtil.contrast(origin, target);
|
|
|
|
|
|
if (result != null) {
|
|
|
|
|
|
// result.getOriginChange() -> 原始对象中发生变化的属性 (x=1)
|
|
|
|
|
|
// result.getTargetChange() -> 修改后对象中发生变化的属性 (x=2)
|
|
|
|
|
|
System.out.println(result.getOriginChange().getX()); // 1
|
|
|
|
|
|
System.out.println(result.getTargetChange().getX()); // 2
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 工具说明
|
|
|
|
|
|
|
|
|
|
|
|
### ContrastUtil(通用对比工具)
|
|
|
|
|
|
|
|
|
|
|
|
`ContrastUtil` 是默认的对象对比实现类,提供静态方法 `contrast(T origin, T target)` 进行对象对比。
|
|
|
|
|
|
|
|
|
|
|
|
**处理规则:**
|
|
|
|
|
|
|
|
|
|
|
|
1. **基本类型和包装类型**:直接使用 `equals` 进行比较(如 `int`、`String`、`Date` 等)
|
|
|
|
|
|
2. **Collection类型**:比较两个集合的差异,返回差集。仅处理一层,泛型内的元素直接调用 `equals` 比较
|
|
|
|
|
|
3. **Map类型**:比较两个Map的差异,包括相同key的value比较和不同key的差异
|
|
|
|
|
|
4. **自定义类型**:调用 `equals` 方法进行比较,**要求重写 `equals` 和 `hashCode` 方法**,否则会比较对象地址
|
|
|
|
|
|
5. **类型一致性**:比较的两个对象类型必须一致,否则抛出 `TypeMisMatchException`
|
|
|
|
|
|
6. **空值处理**:入参可以为 `null`,两者都为 `null` 返回 `null`,其中一个为 `null` 则返回非空一方的结果
|
|
|
|
|
|
7. **Getter/Setter检查**:自定义类型必须有完整的 `get`/`set` 方法,否则抛出 `NoGetMethodException` / `NoSetMethodException`
|
|
|
|
|
|
8. **抽象类和接口**:不支持抽象类和接口的实例对比,会直接返回 `null`
|
|
|
|
|
|
|
|
|
|
|
|
**返回值说明:**
|
|
|
|
|
|
|
|
|
|
|
|
`ContrastObject<T>` 包含四个字段:
|
|
|
|
|
|
- `origin`:原始对象(完整)
|
|
|
|
|
|
- `target`:修改后的对象(完整)
|
|
|
|
|
|
- `originChange`:原始对象中**发生变化**的属性(新实例,仅包含变化的字段)
|
|
|
|
|
|
- `targetChange`:修改后对象中**发生变化**的属性(新实例,仅包含变化的字段)
|
|
|
|
|
|
|
|
|
|
|
|
无变化时返回 `null`。
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
### AbstractContrastUtil(抽象基类)
|
|
|
|
|
|
|
|
|
|
|
|
`AbstractContrastUtil<T>` 提供最大的自定义性,使用者可以通过继承并实现抽象方法来定制各种类型的对比逻辑。
|
|
|
|
|
|
|
|
|
|
|
|
**需要实现的抽象方法:**
|
|
|
|
|
|
|
|
|
|
|
|
| 方法 | 说明 |
|
|
|
|
|
|
|------|------|
|
|
|
|
|
|
| `originContract()` | 基本类型和包装类型的对比逻辑 |
|
|
|
|
|
|
| `selfContract()` | 自定义类型的对比逻辑 |
|
|
|
|
|
|
| `mapContract()` | Map类型的对比逻辑 |
|
|
|
|
|
|
| `collectionContract()` | Collection类型的对比逻辑 |
|
|
|
|
|
|
| `specialContract()` | 特殊类型的自定义处理逻辑 |
|
|
|
|
|
|
| `isSpecialType()` | 判断是否为特殊类型(优先于Collection/Map判断) |
|
|
|
|
|
|
| `nullProcessor()` | 入参为null的处理逻辑 |
|
|
|
|
|
|
| `originProcessor()` | 基本类型入参的处理逻辑 |
|
|
|
|
|
|
| `setContrastObject()` | 组装返回结果对象 |
|
|
|
|
|
|
|
|
|
|
|
|
**使用示例:** 参考 [ContrastUtilExample.java](src/main/java/ContrastUtilExample.java)
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 项目结构
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
src/main/java/
|
|
|
|
|
|
├── ContrastUtil.java # 默认对比工具实现
|
|
|
|
|
|
├── AbstractContrastUtil.java # 抽象对比基类
|
|
|
|
|
|
├── ContrastUtilExample.java # 抽象基类使用示例
|
|
|
|
|
|
├── bean/
|
|
|
|
|
|
│ └── ContrastObject.java # 对比结果包装类
|
|
|
|
|
|
├── constant/
|
|
|
|
|
|
│ └── Constant.java # 常量定义
|
|
|
|
|
|
├── exception/
|
|
|
|
|
|
│ ├── NoGetMethodException.java # 缺少getter方法异常
|
|
|
|
|
|
│ ├── NoSetMethodException.java # 缺少setter方法异常
|
|
|
|
|
|
│ └── TypeMisMatchException.java # 类型不匹配异常
|
|
|
|
|
|
├── handler/
|
|
|
|
|
|
│ ├── CollectionHandler.java # Collection类型处理器
|
|
|
|
|
|
│ ├── MapHandler.java # Map类型处理器
|
|
|
|
|
|
│ ├── OriginHandler.java # 基本类型处理器
|
|
|
|
|
|
│ └── SelfHandler.java # 自定义类型处理器
|
|
|
|
|
|
└── util/
|
|
|
|
|
|
├── CheckUtil.java # 类型检查工具
|
|
|
|
|
|
├── ClassUtil.java # 类反射工具
|
|
|
|
|
|
├── CollectionUtil.java # 集合差异计算工具
|
|
|
|
|
|
├── MapUtil.java # Map工具
|
|
|
|
|
|
└── SetUtil.java # Set工具
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 依赖
|
|
|
|
|
|
|
|
|
|
|
|
| 依赖 | 版本 | 用途 |
|
|
|
|
|
|
|------|------|------|
|
|
|
|
|
|
| commons-lang3 | 3.9 | 通用工具 |
|
|
|
|
|
|
| commons-collections4 | 4.4 | 集合工具 |
|
|
|
|
|
|
| guava | 28.2-jre | Google工具库 |
|
|
|
|
|
|
| reflectasm | 1.11.0 | 高性能反射 |
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 注意事项
|
|
|
|
|
|
|
|
|
|
|
|
1. Collection和Map仅处理一层,嵌套的泛型类型直接调用 `equals` 比较,可能导致结果不准确。如需深度对比,请使用 `AbstractContrastUtil` 自定义处理逻辑
|
|
|
|
|
|
2. 自定义类型**必须重写 `equals` 和 `hashCode` 方法**,否则会比较对象地址而非值
|
|
|
|
|
|
3. 当前使用 reflectASM 进行反射调用,性能优于标准反射,但仍有优化空间
|
|
|
|
|
|
4. 暂不支持 `is` 前缀的 boolean getter 方法(如 `isActive()`),后续版本会支持
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 更新日志
|
|
|
|
|
|
|
|
|
|
|
|
- **2020/4/30**:创建项目,完成初始实现和单元测试
|
|
|
|
|
|
- **2020/5/9**:添加测试方法,修复反射处理null的空指针问题,创建抽象类和继承使用示例
|
|
|
|
|
|
- **2026/5/17**:修复多个bug(MapHandler参数错误、OriginHandler空值比较逻辑、ContrastObject NPE风险等),优化代码实现,补全注释和文档
|