CODE REUSE
即 -> 代码复用性
背景与技术点
背景:写导入excel校验功能,通用性的非空校验方法(当然如果要逐个个性化校验另说)。
技术点:反射和泛型的应用与解析
原代码
/**
* 校验是否存在空数据
* @param x
*/
private void validateBlank(StoreBrandRetentionImportExcelVO x) {
if (StringUtils.isBlank(x.getStoreCode())) {
throw exception(STORE_CODE_IMPORT_IS_EMPTY);
}
if (StringUtils.isBlank(x.getBrand())) {
throw exception(BRAND_IMPORT_IS_EMPTY);
}
if (StringUtils.isBlank(x.getDistributionGrade())) {
throw exception(DISTRIBUTION_GRADE_IMPORT_IS_EMPTY);
}
if (StringUtils.isBlank(x.getStandardRetention())) {
throw exception(STANDARD_RETENTION_IMPORT_IS_EMPTY);
}
}
优化后
/**
* Excel 工具类
*
* @author zhangsheng
*/
public class ExcelUtils {
/**
* 导入Excel 通用校验方法
* @param list
* @return
* @param <T>
*/
public static <T> CommonImportRespVO getCommonImportRespVO(List<T> list) {
if (CollUtil.isEmpty(list)) {
throw exception(IMPORT_LIST_IS_EMPTY);
}
CommonImportRespVO respVO = CommonImportRespVO.builder().createSize(0)
.updateSize(0).failureObjects(new TreeSet<>()).build();
// 校验
for (int i = 0; i < list.size(); i++) {
try {
validateBlank(list.get(i));
} catch (ServiceException ex) {
// 第n行,校验失败
respVO.getFailureObjects().add(String.format("第%s行,校验失败:%s", i + 1, ex.getMessage()));
}
}
return respVO;
}
/**
* 校验操作
*
* @param x
*/
private static <T> void validateProcess(T x) {
Class<?> clazz = x.getClass();
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
Object value;
try {
value = field.get(x);
} catch (Exception e) {
throw exception(IMPORT_ERROR, e.getMessage());
}
if (ImportExcelFieldConstants.BRAND.equals(field.getName())) {// 判断字段名是否为"brand"
if (value instanceof String && !ExcelCheckEnum.BRAND.getName().contains(value.toString())) {
throw exception(BRAND_DATA_IS_ERROR,value);
}
}else{// 非空判断
validateBlank(value);
}
}
}
/**
* 校验是否为空
*
* @param value
*/
private static void validateBlank(Object value) {
if (value instanceof String) {
if (StringUtils.isBlank(value.toString())) {
throw exception(IMPORT_DATA_IS_EMPTY);
}
} else if (Objects.isNull(value)) {
throw exception(IMPORT_DATA_IS_EMPTY);
}
}
}
/**
* @author zhangsheng
*/
@Getter
@AllArgsConstructor
public enum ExcelCheckEnum implements IntArrayValuable {
// 这里写成枚举,可以改成 -> 策略模式+数据库查询+缓存 -> 这边暂时没必要
BRAND(0, "男装 女装 童装 鞋子"),
GENDER(1, "男 女");
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(ExcelCheckEnum::getCode).toArray();
/**
* 值
*/
private final Integer code;
/**
* 名称
*/
private final String name;
@Override
public int[] array() {
return ARRAYS;
}
}
反射
获取 Class<?> 的三种方式
// 伪代码
Class<?> clazz = Class.forName(className);
Class<?> clazz = instance.getClass();
类名.class;
code
/**
* 上文使用Class<?> clazz = Class.forName(className)的方式
* 比较一下Class.forName(className)的方式 -> 不满足我的需求
* @param className
*/
public void getClassByName(String className) {
try {
// 使用 Class.forName() 方法来获取 Class 对象,参数是类的完整限定名
Class<?> clazz = Class.forName(className);
// 打印类的名称
System.out.println("Class Name: " + clazz.getName());
// 打印类的所有方法名
Method[] methods = clazz.getMethods();
for (Method method : methods) {
System.out.println("Method Name: " + method.getName());
}
// 打印类的所有字段名
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
System.out.println("Field Name: " + field.getName());
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
泛型
在编译期不确定参数类型,在运行期确定。方法签名的时候标记为泛型,类型根据传参来确认。
- 是泛型的声明,在类中跟在类名之后,在方法上在访问修饰符后,返回值前。
//demo1(类)
public interface List<E> extends Collection<E> {//...}
//demo2(方法)
private <T> void validateBlank(T x) {//...}
- T是对于泛型的使用,常见于传参、返回值等。
//同上
private <T> void validateBlank(T x) {//...}
<?>是无界通配符 -> 有界通配符<? extends 类型>和<? super 类型>
无界通配符注意点:
在使用 <?>
通配符类型时,确实无法添加具体类型的元素到集合中,只能进行与类型无关的操作,例如遍历或删除元素。这样可以确保泛型的类型安全性,避免出现类型错误或转换异常。
// 无界通配符的demo
// demo1(正面)
List<?> list = new ArrayList<>();
List<Integer> integers = new ArrayList<>();
integers.add(1);
integers.add(2);
list = integers; // 可以将具体类型集合赋值给通配符类型集合
for (Object item : list) {
System.out.println(item); // 可以遍历集合并打印元素
}
// demo2(反面)
List<?> list = new ArrayList<>();
list.add(1); // 运行时异常:UnsupportedOperationException
有界通配符注意点:
与通配符 <?> 不同,<? super 类型> 用于界定泛型下界,因此我们可以向这种集合中添加特定类型的元素,但在读取元素时的类型信息会模糊。由于我们不知道具体的类型是什么,所以只能以 Object 类型进行处理。
// 有界通配符的demo
// demo1(正面)
List<? super Integer> list = new ArrayList<>();
list.add(1); // 可以添加具体类型 Integer 的元素
list.add(2); // 可以添加具体类型 Integer 的元素
Object item = list.get(0); // 读取元素时的类型为 Object
System.out.println(item); // 输出: 1
// demo2(反面)
List<? super Integer> list = new ArrayList<>();
list.add(1); // 可以添加具体类型 Integer 的元素
list.add("hello"); // 编译错误:无法添加不符合下界的类型
以上是全部内容,感谢阅读。