怎么样能把函数式接口用好|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);
    }
}


相关文章
|
15天前
|
Java API Maven
如何使用Java开发抖音API接口?
在数字化时代,社交媒体平台如抖音成为生活的重要部分。本文详细介绍了如何用Java开发抖音API接口,从创建开发者账号、申请API权限、准备开发环境,到编写代码、测试运行及注意事项,全面覆盖了整个开发流程。
59 10
|
19天前
|
JSON Java Apache
非常实用的Http应用框架,杜绝Java Http 接口对接繁琐编程
UniHttp 是一个声明式的 HTTP 接口对接框架,帮助开发者快速对接第三方 HTTP 接口。通过 @HttpApi 注解定义接口,使用 @GetHttpInterface 和 @PostHttpInterface 等注解配置请求方法和参数。支持自定义代理逻辑、全局请求参数、错误处理和连接池配置,提高代码的内聚性和可读性。
|
12天前
|
安全 Java 开发者
Java 多线程并发控制:深入理解与实战应用
《Java多线程并发控制:深入理解与实战应用》一书详细解析了Java多线程编程的核心概念、并发控制技术及其实战技巧,适合Java开发者深入学习和实践参考。
|
10天前
|
Java
在Java中,接口之间可以继承吗?
接口继承是一种重要的机制,它允许一个接口从另一个或多个接口继承方法和常量。
37 1
|
22天前
|
SQL 安全 Java
安全问题已经成为软件开发中不可忽视的重要议题。对于使用Java语言开发的应用程序来说,安全性更是至关重要
在当今网络环境下,Java应用的安全性至关重要。本文深入探讨了Java安全编程的最佳实践,包括代码审查、输入验证、输出编码、访问控制和加密技术等,帮助开发者构建安全可靠的应用。通过掌握相关技术和工具,开发者可以有效防范安全威胁,确保应用的安全性。
44 4
|
20天前
|
Java
java线程接口
Thread的构造方法创建对象的时候传入了Runnable接口的对象 ,Runnable接口对象重写run方法相当于指定线程任务,创建线程的时候绑定了该线程对象要干的任务。 Runnable的对象称之为:线程任务对象 不是线程对象 必须要交给Thread线程对象。 通过Thread的构造方法, 就可以把任务对象Runnable,绑定到Thread对象中, 将来执行start方法,就会自动执行Runable实现类对象中的run里面的内容。
36 1
|
23天前
|
缓存 监控 Java
如何运用JAVA开发API接口?
本文详细介绍了如何使用Java开发API接口,涵盖创建、实现、测试和部署接口的关键步骤。同时,讨论了接口的安全性设计和设计原则,帮助开发者构建高效、安全、易于维护的API接口。
57 4
|
26天前
|
Java 开发者
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
44 4
|
29天前
|
SQL Java 程序员
倍增 Java 程序员的开发效率
应用计算困境:Java 作为主流开发语言,在数据处理方面存在复杂度高的问题,而 SQL 虽然简洁但受限于数据库架构。SPL(Structured Process Language)是一种纯 Java 开发的数据处理语言,结合了 Java 的架构灵活性和 SQL 的简洁性。SPL 提供简洁的语法、完善的计算能力、高效的 IDE、大数据支持、与 Java 应用无缝集成以及开放性和热切换特性,能够大幅提升开发效率和性能。
|
29天前
|
存储 Java 关系型数据库
在Java开发中,数据库连接是应用与数据交互的关键环节。本文通过案例分析,深入探讨Java连接池的原理与最佳实践
在Java开发中,数据库连接是应用与数据交互的关键环节。本文通过案例分析,深入探讨Java连接池的原理与最佳实践,包括连接创建、分配、复用和释放等操作,并通过电商应用实例展示了如何选择合适的连接池库(如HikariCP)和配置参数,实现高效、稳定的数据库连接管理。
52 2