如何校验按指定字段排序的TransactionDto集合并规避泛型警告?
解决REST接口排序校验的泛型警告问题
实用方案
既然TransactionDto所有字段(包括自定义枚举Currency)都实现了Comparable,完全可以避开反射带来的泛型警告,或者给反射加类型约束,下面是几个靠谱的思路:
方案一:用类型安全的字段提取器(首推)
直接给每个排序字段写个字段提取函数,彻底不用反射,类型安全还没警告:
// 先把所有需要排序的字段提取器存起来 private static final Map<String, Function<TransactionDto, ? extends Comparable<?>>> FIELD_GETTERS = Map.of( "id", TransactionDto::getId, "amount", TransactionDto::getAmount, "currency", TransactionDto::getCurrency, "createTime", TransactionDto::getCreateTime ); // 校验排序逻辑的方法 public void checkSortOrder(List<TransactionDto> transactions, String sortField, SortOrder sortOrder) { Function<TransactionDto, ? extends Comparable<?>> getter = FIELD_GETTERS.get(sortField); // 生成比较器 Comparator<TransactionDto> comparator = Comparator.comparing(getter); // 倒序的话反转比较器 if (sortOrder == SortOrder.DESC) { comparator = comparator.reversed(); } // 用Guava的方法校验顺序 assertTrue(Comparators.isInOrder(transactions, comparator)); }
- 好处:完全没泛型警告,代码一眼看懂,性能比反射好太多。
- 小缺点:字段多的话要挨个写提取器,但测试代码维护起来比反射省心多了,毕竟测试用例要稳定。
方案二:给反射加泛型约束
如果非得用反射,就把反射逻辑封装成泛型方法,只在最底层抑制警告,别让警告到处飘:
// 封装反射获取字段值的方法,明确返回Comparable类型 @SuppressWarnings("unchecked") private <T extends Comparable<T>> T getFieldVal(TransactionDto dto, String fieldName) throws Exception { String getterName = "get" + Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1); Method getter = TransactionDto.class.getMethod(getterName); return (T) getter.invoke(dto); } // 校验排序 public void checkSortWithReflection(List<TransactionDto> transactions, String sortField, SortOrder sortOrder) throws Exception { Comparator<TransactionDto> comparator = (d1, d2) -> { Comparable<?> val1 = getFieldVal(d1, sortField); Comparable<?> val2 = getFieldVal(d2, sortField); return val1.compareTo(val2); }; if (sortOrder == SortOrder.DESC) { comparator = comparator.reversed(); } assertTrue(Comparators.isInOrder(transactions, comparator)); }
- 这里通过泛型方法
getFieldVal明确返回T extends Comparable<T>,@SuppressWarnings("unchecked")只加在这个方法上,不会影响其他代码。 - 注意:题目里已经说所有字段都实现了
Comparable,所以这里强制转换是安全的,不会出运行时异常。
方案三:缓存反射方法提升性能
如果字段多又要用反射,提前把getter方法缓存起来,避免每次反射都去查方法,同时配合泛型约束:
// 提前缓存所有getter方法,省得每次反射 private static final Map<String, Method> GETTER_CACHE = new HashMap<>(); static { for (Field field : TransactionDto.class.getDeclaredFields()) { String getterName = "get" + Character.toUpperCase(field.getName().charAt(0)) + field.getName().substring(1); try { GETTER_CACHE.put(field.getName(), TransactionDto.class.getMethod(getterName)); } catch (NoSuchMethodException e) { throw new RuntimeException("找不到字段" + field.getName() + "的getter方法", e); } } } @SuppressWarnings("unchecked") private <T extends Comparable<T>> T getCachedFieldVal(TransactionDto dto, String fieldName) throws Exception { Method getter = GETTER_CACHE.get(fieldName); return (T) getter.invoke(dto); }
- 缓存之后反射性能提升不少,同样用泛型方法约束返回类型,消除警告。
总结
优先选方案一,测试用例要的就是稳定和可读性;如果必须用反射,就选方案二或三,把警告控制在最小范围,别让代码里全是黄色警告条。
内容的提问来源于stack exchange,提问作者Jason
相关产品推荐
相关产品推荐

