如何用Java泛型及高级特性消除同类参数方法的重复逻辑
问题:合并重复的过滤方法(限制特定方案)
现有两个逻辑完全一致的filterProcessableItems方法,分别处理List<TypeAItem>和List<TypeBItem>类型的参数。TypeAItem与TypeBItem均继承自GeneralItem,二者拥有date1、date2、price1等相同字段,方法仅针对这些相同字段进行处理。代码如下:
import java.time.LocalDate; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class FilterUtils { private static final Logger log = LoggerFactory.getLogger(FilterUtils.class); public static List<TypeAItem> filterProcessableItems(List<TypeAItem> items) { return items.stream().map(item -> { if (Objects.isNull(item.getDate1())) { log.error("Date1 is null {}", item); return null; } if (Objects.isNull(item.getDate2())) { log.error("Date2 is null {}", item); return null; } if (Objects.isNull(item.getPrice1())) { log.error("price1 is null {}", item); return null; } return item; }).filter(Objects::nonNull) .collect(Collectors.toList()); } public static List<TypeBItem> filterProcessableItems(List<TypeBItem> items) { return items.stream().map(item -> { if (Objects.isNull(item.getDate1())) { log.error("Date1 is null {}", item); return null; } if (Objects.isNull(item.getDate2())) { log.error("Date2 is null {}", item); return null; } if (Objects.isNull(item.getPrice1())) { log.error("price1 is null {}", item); return null; } return item; }).filter(Objects::nonNull) .collect(Collectors.toList()); } } class GeneralItem {} class TypeAItem extends GeneralItem { private LocalDate date1; private LocalDate date2; private Integer price1; private int price2; // many more fields public LocalDate getDate1() { return date1; } public LocalDate getDate2() { return date2; } public Integer getPrice1() { return price1; } } class TypeBItem extends GeneralItem { private LocalDate date1; private LocalDate date2; private Integer price1; private int price2; // many more fields public LocalDate getDate1() { return date1; } public LocalDate getDate2() { return date2; } public Integer getPrice1() { return price1; } }
要求将这两个方法合并为一个实现相同逻辑的方法,且不可使用以下两种方案:
- 将相同字段移至父类
GeneralItem; - 使用多个
instanceof判断并强制类型转换。
解决方案
当然有可行的Java技术方案,以下几种方式完全符合你的限制条件:
方案1:泛型 + 函数式接口(类型安全,推荐)
核心思路是把字段的获取逻辑抽象成函数式接口参数,让通用方法只聚焦校验逻辑,不依赖具体实体类型。
import java.time.LocalDate; import java.util.List; import java.util.Objects; import java.util.function.Function; import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class FilterUtils { private static final Logger log = LoggerFactory.getLogger(FilterUtils.class); // 通用过滤方法 public static <T> List<T> filterProcessableItems(List<T> items, Function<T, LocalDate> date1Getter, Function<T, LocalDate> date2Getter, Function<T, Integer> price1Getter) { return items.stream() .map(item -> { if (Objects.isNull(date1Getter.apply(item))) { log.error("Date1 is null {}", item); return null; } if (Objects.isNull(date2Getter.apply(item))) { log.error("Date2 is null {}", item); return null; } if (Objects.isNull(price1Getter.apply(item))) { log.error("price1 is null {}", item); return null; } return item; }) .filter(Objects::nonNull) .collect(Collectors.toList()); } // 调用示例 public static void main(String[] args) { List<TypeAItem> typeAItems = List.of(new TypeAItem()); List<TypeBItem> typeBItems = List.of(new TypeBItem()); List<TypeAItem> filteredA = filterProcessableItems(typeAItems, TypeAItem::getDate1, TypeAItem::getDate2, TypeAItem::getPrice1); List<TypeBItem> filteredB = filterProcessableItems(typeBItems, TypeBItem::getDate1, TypeBItem::getDate2, TypeBItem::getPrice1); } }
这个方案的优势:
- 完全符合限制条件,既不用移动父类字段,也不需要
instanceof; - 编译期类型安全,能及时发现getter引用错误;
- 性能和原有方法一致,没有额外开销。
方案2:反射实现(无需修改实体类,注意适用场景)
如果不想额外定义接口或传递函数参数,可以用反射读取字段值实现通用校验。需要注意字段的访问权限和类型匹配(比如原代码中price1如果是int类型,本身不能为null,需要调整判断逻辑)。
import java.lang.reflect.Field; import java.time.LocalDate; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class FilterUtils { private static final Logger log = LoggerFactory.getLogger(FilterUtils.class); public static <T> List<T> filterProcessableItems(List<T> items) { return items.stream() .map(item -> { try { // 检查date1字段 Field date1Field = item.getClass().getDeclaredField("date1"); date1Field.setAccessible(true); if (Objects.isNull(date1Field.get(item))) { log.error("Date1 is null {}", item); return null; } // 检查date2字段 Field date2Field = item.getClass().getDeclaredField("date2"); date2Field.setAccessible(true); if (Objects.isNull(date2Field.get(item))) { log.error("Date2 is null {}", item); return null; } // 检查price1字段(若为Integer类型则直接判空,int类型需定义无效值逻辑) Field price1Field = item.getClass().getDeclaredField("price1"); price1Field.setAccessible(true); if (Objects.isNull(price1Field.get(item))) { log.error("price1 is null {}", item); return null; } } catch (NoSuchFieldException | IllegalAccessException e) { log.error("Failed to validate item fields", e); return null; } return item; }) .filter(Objects::nonNull) .collect(Collectors.toList()); } }
注意事项:
- 反射需要调用
setAccessible(true)来访问私有字段; - 性能略低于直接调用getter,适合字段固定、调用频率不高的场景;
- 基础类型(如
int)不能为null,需要根据实际业务定义无效值判断逻辑。
方案3:定义公共接口(类型安全,需修改实体类)
如果允许修改TypeAItem和TypeBItem的代码,可以定义一个包含所需字段getter的接口,让两个类实现该接口,通用方法直接处理接口类型的列表。
import java.time.LocalDate; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; // 定义公共接口,声明需要的getter方法 public interface ProcessableItem { LocalDate getDate1(); LocalDate getDate2(); Integer getPrice1(); } // 修改TypeAItem实现接口 class TypeAItem extends GeneralItem implements ProcessableItem { private LocalDate date1; private LocalDate date2; private Integer price1; private int price2; // many more fields @Override public LocalDate getDate1() { return date1; } @Override public LocalDate getDate2() { return date2; } @Override public Integer getPrice1() { return price1; } } // 修改TypeBItem实现接口 class TypeBItem extends GeneralItem implements ProcessableItem { private LocalDate date1; private LocalDate date2; private Integer price1; private int price2; // many more fields @Override public LocalDate getDate1() { return date1; } @Override public LocalDate getDate2() { return date2; } @Override public Integer getPrice1() { return price1; } } // 通用过滤方法 public class FilterUtils { private static final Logger log = LoggerFactory.getLogger(FilterUtils.class); public static List<? extends ProcessableItem> filterProcessableItems(List<? extends ProcessableItem> items) { return items.stream() .map(item -> { if (Objects.isNull(item.getDate1())) { log.error("Date1 is null {}", item); return null; } if (Objects.isNull(item.getDate2())) { log.error("Date2 is null {}", item); return null; } if (Objects.isNull(item.getPrice1())) { log.error("price1 is null {}", item); return null; } return item; }) .filter(Objects::nonNull) .collect(Collectors.toList()); } }
这个方案的优势:
- 类型安全,代码简洁易维护;
- 完全符合限制条件,没有移动父类字段,也不需要
instanceof; - 调用时无需额外传递参数,直接传入列表即可。
内容的提问来源于stack exchange,提问作者ym_wp
相关产品推荐
相关产品推荐

