You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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; }
}

要求将这两个方法合并为一个实现相同逻辑的方法,且不可使用以下两种方案:

  1. 将相同字段移至父类GeneralItem;
  2. 使用多个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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 17:54:55