日常开发中使用的函数式接口

1. 四大核心函数式接口

1.1 Consumer

消费型接口,有T类型的入参,无反参

void accept(T t);

1.2 Supplier

供给型接口,无入参,有T类型的反参

T get();

1.3 Function

函数式接口,有T类型的入参,有R类型的反参

R apply(T t);

1.4 Predicate

断言型接口,有T类型的入参,有boolean类型的反参

boolean test(T t);

2. 其他函数接口

2.1 BiConsumer

两个入参的消费型接口,T,U

void accept(T t, U u);

2.2 BiFunction

两个入参的函数式接口,入参T,U,反参R

R apply(T t, U u);

2.3 UnaryOperator

对参数类型T进行操作,入参T,反参T,为Function的子接口

@FunctionalInterface
public interface UnaryOperator<T> extends Function<T, T> {

    static <T> UnaryOperator<T> identity() {
        return t -> t;
    }
}

3. 实际应用

3.1 痛点

使用EasyExcel进行excel进行导入

EasyExcel的 读操作 需要实现 ReadListener 接口以及定义一个映射 T 泛型存储数据行的实体。


如:示例中的 DemoDataListenerDemoData

public class DemoDataListener implements ReadListener<DemoData> {
    // 省略具体的方法实现
}


由于每次导入都要写一个Listener,非常麻烦,就想有没有一个能减少劳动力的方法呢?


3.2 实现解决

就利用了消费型接口 Consumer<T> 去做了实现

3.2.1 实现ReadListener,这里实现的是抽象类AnalysisEventListener,也是一个ReadListener
public class EasyExcelConsumerListener<T> extends AnalysisEventListener<T> {
    private final List<T> list;
    private final Consumer<List<T>> consumer;

    public EasyExcelConsumerListener(Consumer<List<T>> consumer){
        this.consumer = consumer;
        list = new ArrayList<>();
    }

    @Override
    public void invoke(T data, AnalysisContext context) {
        list.add(data);
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        consumer.accept(list);
    }
}
3.2.2 扩展EasyExcel
public class EasyExcelUtil extends EasyExcel {

    private EasyExcelUtil() {
    }
    public static <T> ExcelReaderBuilder read(InputStream inputStream, Class<T> head, Consumer<List<T>> consumer) {
        return read(inputStream, head, new EasyExcelConsumerListener<>(consumer));
    }
}    
3.2.3 使用

Controller 层的代码实现,其中 ExcelData.class 是需要映射的数据实体类,这个映射实体类好像不能投机取巧了~~~

@RestContoller
@RequestMapping("/excel")
public class ExcelController {
    
    @Autowired
    private IExcelService service;
    
    @GetMapping("/importExcel")
    public R importExcel(MultipartFile file) throws IOException {
        // service层执行具体的importExcel方法实现
        EasyExcelUtil.read(file.getInputStream(), ExcelData.class, service::importExcel).sheet().doRead();
        return R.success("导入成功!");
    }
}

这样就可以关注具体的业务实现啦


3.2.4 使用两个参数的 ​BiConsumer<T, U>​,与上面的 ​Consumer<T>​ 异曲同工

实现 ​Listener

/**
 * 异步导入
 * 由于获取不到当前登录用户,通过参数传进来
 */
public class AsyncBiConsumerListener<T> extends AnalysisEventListener<T> {
    private final Long userId;
    private final List<T> list;
    private final BiConsumer<List<T>, Long> biConsumer;

    public AsyncBiConsumerListener(BiConsumer<List<T>, Long> biConsumer, Long userId){
        this.userId = userId;
        this.biConsumer = biConsumer;
        list = new ArrayList<>();
    }

    @Override
    public void invoke(T data, AnalysisContext context) {
        list.add(data);
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        biConsumer.accept(list, userId);
    }
}

扩展 ​EasyExcel

public class EasyExcelUtil extends EasyExcel {

    private EasyExcelUtil() {
    }

    public static <T> ExcelReaderBuilder read(InputStream inputStream, Class<T> head, Consumer<List<T>> consumer) {
        return read(inputStream, head, new EasyExcelConsumerListener<>(consumer));
    }
    public static <T> ExcelReaderBuilder read(InputStream inputStream, Class<T> head, BiConsumer<List<T>, Long> biConsumer, Long userId) {
        return read(inputStream, head, new AsyncBiConsumerListener<>(biConsumer, userId));
    }
}

使用

public void importExcel(InputStream file, String key, Long userId) {
    try {
        EasyExcelUtil.read(file, ExcelData.class, service::importExcel, userId).sheet().doRead();
    } catch (Exception ex) {
        log.error("异步导入异常:", ex);
    }
}
上一篇:YOLO 人脸识别算法以及应用 (1)


下一篇:TimSort排序算法及一个问题分析