怎么样能把函数式接口用好|Java 开发实战

简介: JDK8大家知道并使用的特性莫过于Lambda,这个很多人会用,比如Stream流,但都是简单浅显的使用,比如调用集合的Stream API等,但不会自己定义函数接口或API,今天通过几个案例来提高下对Java 中函数式编程的使用。

开篇

JDK8大家知道并使用的特性莫过于Lambda,这个很多人会用,比如Stream流,但都是简单浅显的使用,比如调用集合的Stream API等,但不会自己定义函数接口或API,今天通过几个案例来提高下对Java 中函数式编程的使用。

案例演示

函数接口说明

接口 输入参数 返回类型 说明
UnaryOperator T T 一元函数,输入输出类型相同
Predicate T boolean 断言
Consumer T / 消费一个数据,只有输入没有输出
Function<T,R> T R 输入 T 返回 R,有输入也有输出
Supplier / T 提供一个数据,没有输入只有输出
BiFunction<T,U,R> (T,U) R 两个输入参数
BiPredicate<L, R> (L,R) boolean 两个输入参数
BiConsumer<T, U> (T,U) void 两个输入参数
BinaryOperator (T,T) T 二元函数,输入输出类型相同

案例一

Function<Integer, Integer> times2 = e -> e * 2;
Function<Integer, Integer> squared = e -> e * e;
// 先执行参数,再执行调用者
/*
 * 1. 4 * 4 = 16 16 * 2 = 32
 */
System.out.println("result: " + times2.compose(squared).apply(4)); // 32
/*
 * 先执行调用者: 4 * 2 = 8 再执行then传入的function 8 * 8 = 64
 */
System.out.println("result: " + times2.andThen(squared).apply(4)); // 64
复制代码

这两个方法,andThen表示在外面那个Function执行之后调用,compose表示在外面那个Function执行之前调用。

Tests passed: 1 of 1 test - 46 ms
result:32
result:64

案例二

@Test
public void test2(){
    // 3个数相加
    Function<Integer, Function<Integer, IntFunction<Integer>>> addfun3 = x -> y -> z -> x + y + z ;
    // 7个数相加
    Function<Integer,
            Function<Integer,
                    Function<Integer,
                            Function<Integer,
                                    Function<Integer,
                                            Function<Integer, IntFunction<Integer>>>>>>>
            addfun7 = x -> y -> z -> a -> c -> b -> d -> x + y + z + a + c + b + d;
    // 1+2+3
    Integer sum = addfun3.apply(1).apply(2).apply(3);
    System.out.println(sum);
}
复制代码

通过这种方式,可以实现链式编程的累加效果

Tests passed: 1 of 1 test- 89r

案例三

@SafeVarargs
private static <R> Function<R, R> combineFunctions(Function<R, R>... functions) {
    return Arrays.stream(functions)
            .reduce(Function::andThen)
            .orElseThrow(() -> new IllegalArgumentException("No functions to combine"));
}
@Test
public void test3() {
    Function<Integer, Integer> addfun2 = x -> x * x;
    final Integer apply = combineFunctions(addfun2, addfun2).apply(2);
    System.out.println(apply);
    String str = "1,2,3,4,5,6";
    Function<Object, Object> splitByComma = s -> ((String) s).split(",");
    Function<Object, Object> convertToInt = tokens -> Stream.of((String[]) tokens).map(Integer::valueOf).toArray(Integer[]::new);
    Function<Object, Object> findMax = ints -> Stream.of((Integer[]) ints).max(Integer::compare).get();
    Integer max = (Integer) combineFunctions(splitByComma, convertToInt, findMax).apply(str);
    System.out.println(max);
}
复制代码

如果你想有一个接受可变长度的Function参数的方法,可以使用...描述符,其中泛型<R,R>代表输入与输入类型,使用泛型是为了兼容性更高

Tests passed: 1 of 1 test - 59 ms
16

案例四

@Test
public void test4(){
    Function<Integer, Integer> addfun2 = x -> x * x;
    final Calculator<Integer, Integer> calculator = new Calculator<>(2);
    final Integer integer = calculator.combineFunctions(addfun2, addfun2);
    System.out.println(integer);
}
public class Calculator<R,T> {
    // 被操作的属性
    private Object input;
    public Calculator(Object input) {
        this.input = input;
    }
    // 可以通过这种方式定义对象自身的行为
    @SuppressWarnings("unchecked")
    @SafeVarargs
    public final R combineFunctions(Function<T, T>... functions) {
        return (R) Arrays.stream(functions)
                .reduce(Function::andThen)
                .orElseThrow(() -> new IllegalArgumentException("No functions to combine"))
                .apply((T)input);
    }
}

领域开发模式或多或少都听过、了解过,对象除了有自身的属性之外还可以有自身的行为,针对对象的行为方法也是可以使用函数式编程范式来定义的。

Tests passed: 1 of 1 test - 47 m
16

案例五

// BiConsumer<T, Integer> 两个输入参数T、Integer
 public static <T> Consumer<T> consumerWithIndex(BiConsumer<T, Integer> consumer) {
    class Obj {
        int i;
    }
    // 只会被调用一次,原因看java.lang.Iterable#forEach
    Obj obj = new Obj();
    // 返回的Consumer函数
    return t -> {
        int index = obj.i++;
        // 这里执行System.out.println("list[" + index + "]=" + item),消费指定泛型的数据。
        consumer.accept(t, index);
    };
}
@Test
public void test5(){
    val list = Arrays.asList("Hi", "I", "am", "Henry.Yao");
    // 2个元素为一组
    val partition = Lists.partition(list, 2);
    partition.forEach(LambdaUtils.consumerWithIndex((item, index) -> {
        System.out.println("list[" + index + "]=" + item);
    }));
}
复制代码

Java8的forEach()循环对象的时候,是没有办法得到对象索引下标的,针对这个情况,可以声明一个函数式方法来做到,最终书写形式类似Scala、js的forEach语法,很有用!

Tests passed: 1 of 1 test -88 ms
list[0]=[Hi,I]
list[1]=[am, Henry.Yao]

java.lang.Iterable#forEach

// action:是一个Consumer函数
default void forEach(Consumer<? super T> action) {
    Objects.requireNonNull(action);
    for (T t : this) {
        /* 这里会循环调用Consumer函数,而consumerWithIndex返回的Consumer函数内容为
        t -> {
            int index = obj.i++;
            consumer.accept(t, index);
        }
        所以Obj obj = new Obj()只会被调用一次,这样就不用担心new Obj 时把 i 没有重置为0,不会发生的
        */
        consumer.accept(t, index);
    }
        action.accept(t);
    }
}


相关文章
|
3天前
|
安全 Java 调度
Java线程:深入理解与实战应用
Java线程:深入理解与实战应用
20 0
|
23小时前
|
消息中间件 缓存 NoSQL
Java多线程实战-CompletableFuture异步编程优化查询接口响应速度
Java多线程实战-CompletableFuture异步编程优化查询接口响应速度
|
1天前
|
存储 安全 Java
[Java基础面试题] Map 接口相关
[Java基础面试题] Map 接口相关
|
2天前
|
设计模式 存储 前端开发
Java从入门到精通:2.2.1学习Java Web开发,了解Servlet和JSP技术,掌握MVC设计模式
Java从入门到精通:2.2.1学习Java Web开发,了解Servlet和JSP技术,掌握MVC设计模式
|
7天前
|
IDE Java 数据库连接
使用 Java 进行桌面应用开发
【4月更文挑战第19天】Java 是一款广泛应用于企业级、网络和桌面应用开发的编程语言。其跨平台特性使Java程序能在不同操作系统上运行,而JDK提供了开发所需工具和库。使用Swing等GUI库构建用户界面,结合JDBC进行数据库操作,Socket实现网络通信。虽然面临性能和用户体验的挑战,但通过优化和选用合适的IDE,Java仍能开发出高效稳定的桌面应用。
|
7天前
|
存储 Java 数据库连接
java DDD 领域驱动设计思想的概念与实战
【4月更文挑战第19天】在Java开发中,领域驱动设计(Domain-Driven Design, DDD) 是一种软件设计方法论,强调以领域模型为中心的软件开发。这种方法通过丰富的领域模型来捕捉业务领域的复杂性,并通过软件满足核心业务需求。领域驱动设计不仅是一种技术策略,而且还是一种与业务专家紧密合作的思维方式
25 2
|
7天前
|
Java 开发者
探索 Java 的函数式接口和 Lambda 表达式
【4月更文挑战第19天】Java 中的函数式接口和 Lambda 表达式提供了简洁、灵活的编程方式。函数式接口有且仅有一个抽象方法,用于与 Lambda(一种匿名函数语法)配合,简化代码并增强可读性。Lambda 表达式的优点在于其简洁性和灵活性,常用于事件处理、过滤和排序等场景。使用时注意兼容性和变量作用域,它们能提高代码效率和可维护性。
|
7天前
|
前端开发 Java Go
开发语言详解(python、java、Go(Golong)。。。。)
开发语言详解(python、java、Go(Golong)。。。。)
|
8天前
|
Java
Java接口中可以定义哪些方法?
【4月更文挑战第13天】
14 0
Java接口中可以定义哪些方法?
|
8天前
|
人工智能 前端开发 Java
Java语言开发的AI智慧导诊系统源码springboot+redis 3D互联网智导诊系统源码
智慧导诊解决盲目就诊问题,减轻分诊工作压力。降低挂错号比例,优化就诊流程,有效提高线上线下医疗机构接诊效率。可通过人体画像选择症状部位,了解对应病症信息和推荐就医科室。
147 10