Java8之Function函数、BiFunction函数详解

众所周知,Java8提供了一下非常重要的函数式接口。今天我们就来讲讲其中一个函数式接口-----Function接口。
下面的代码就是Function接口的全部代码。接下来我们逐个分析一下。

@FunctionalInterface
public interface Function<T, R>

@FunctionalInterface 表明该接口是一个函数式接口
<T, R> T 代表参数,R 代表返回值。

第一个方法------R apply(T t)

示例代码:

public class FunctionTest {
    public static void main(String[] args) {
        FunctionTest functionTest = new FunctionTest();
        System.out.println(functionTest.compute(1, value -> 2 * value));
    }
    public int compute(int a, Function<Integer, Integer> function) {
        int result = function.apply(a);
        return result;
    }
}

上述代码展示的就是Function接口中apply方法的使用,其中通过 functionTest.compute方法将两个参数传过去,在compute方法中,function使用参数进行运算。

第二个方法 ------- compose

这个方法是一个默认方法。为了方便在接口中实现具体逻辑,而添加的关键字。
我们可以看到compose方法有一个输入参数为Function类型的值,并且也会返回一个Function类型的值,重点我们看最后一句就会发现,它是先调用参数的apply 方法 再调用调用者的apply 方法

default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }

通过上面的分析,可以得出结果----12,即对参数先执行 value * value,在执行 value * 3。

System.out.println(functionTest2.compute(2, value -> value * 3, value -> value * value));

public int compute(int a, Function<Integer, Integer> function1, Function<Integer, Integer> function2) {
        return function1.compose(function2).apply(a);
    }
第三个方法 ------ andThen

大家是不是觉得这个方法和上面的compose方法差不多,唯一的差别就是最后一句。该方法执行的顺序就是先执行调用者的apply方法,在执行参数的apply方法。(与compose方法相反)

default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }

示例代码
经过上面的分析,我们很容易得出结果,值为36。

System.out.println(functionTest2.compute(2, value -> value * 3, value -> value * value));

public int compute2(int a, Function<Integer, Integer> function1, Function<Integer, Integer> function2) {
        return function1.andThen(function2).apply(a);
    }
第四个方法 ------ identity
static <T> Function<T, T> identity() {
        return t -> t;
    }

该行代码因为实现了Function中唯一的抽象方法,所以通过下面这行代码实例化出了一个 “接口” 对象,同样也会输出helloworld

Function<String, String> fun1 = Function.identity();
        String str1 = fun1.apply("helloWorld");
        System.out.println(str1);

看完了Function,相信大家在学习Java8的时候,都看到了还有一个叫BiFunction的类,下面我们来分析一下BiFunction

什么是BiFunction
@FunctionalInterface
public interface BiFunction<T, U, R>

它也是一个函数式接口,它是Function 的 另一种形式,它接收两个参数(T,U),返回一个参数(R)。那么接下来我们看看这个接口里面有哪些方法吧。

第一个方法 ------ apply
R apply(T t, U u);

示例代码:

System.out.println(functionTest2.compute4(2, 3, (value1, value2) -> value1 + value2));

public int compute4(int a, int b, BiFunction<Integer, Integer, Integer> biFunction) {
        return biFunction.apply(a, b);
    }

上述代码,使用的就是apply方法,我们发现,其实它跟Function里面的apply其实一样,也就是多了一个参数而已。接下来,我们看第二个方法。

第二个方法 ----- andThen

源码:

default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t, U u) -> after.apply(apply(t, u));
    }

我们发现,andThen方法里面的参数是Function类型的,那为什么不能是BiFunction类型的呢。
andThen是先执行调用者的apply,也就是说他先调用BiFunction的apply方法,再去调用参数的apply方法;其中根据Java的规范,一个方法,只能有一个返回值。所以当执行参数的apply方法的时候,只能有一个参数,所以andThen方法里面的参数必须是Function类型的。

那么大家可能会问了,为什么BiFunction里面没有compose方法。
根据Function接口里面的compose方法,我们可以分析出,它是先执行参数的apply方法,在执行调用者的apply方法。
假如BiFunction里面有compose,又因为BiFunction接受两个参数,那么通过上面的分析,可以得出,它的compose方法的参数必定是BiFunction类型的,但是下一步执行调用者的apply方法的时候,因为只有一个参数,所以是无法满足的。所以BiFunction里面没有compose方法。



源码:

public interface Function<T, R> {
 
    /**
     * 将此函数应用于给定参数
     * 真正执行函数接口的方法
     */
    R apply(T t);
 
    /**
     * 函数链,before执行的结果做根函数为参数
     */
    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }
 
    /**
     * 函数链,根函数执行结果做为after的参数
     */
    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }
 
    /**
     * 返回一个参数做为返回值的函数
     */
    static <T> Function<T, T> identity() {
        return t -> t;
    }
}

看注释有些绕,还是看例子

//例1
public class FunctionDemo {
    public static void main(String[] args) {
        Function<Integer,Integer> root = (v) -> {
            System.out.println("root apply");
            return v + 1;// 12
        };
        Function<Integer,Integer> before = (v) -> {
            System.out.println("before apply");
            return v + 1;// 先得结果11,再传给root
        };
 
        System.out.println(root.compose(before).apply(10));
        // before apply
        // root apply
        // 12
    }
}
//例2
public class FunctionDemo {
    public static void main(String[] args) {
        Function<Integer,Integer> root = (v) -> {
            System.out.println("root apply");
            return v + 1;// 先得结果11,再传给after
        };
        Function<Integer,Integer> after = (v) -> {
            System.out.println("after apply");
            return v + 1;// 12
        };
 
        System.out.println(root.andThen(after).apply(10));
        // root apply
        // after apply
        // 12
    }
}

例1和例2虽然参数一样,结果一样,但是相对与根函数root执行的顺序一个在前,一个在后。

Function与Consumer接口都有andThen方法,两者的区别是对参数的使用方式不同。每Consumer使用的是同一个原始参数,Function的参数只会被根函数使用一次,之后的函数使用的是前一个函数的结果做为参数。

了解了Function,其他的也就一目了然了:

BiFunction<T,U,R>:代表了一个接受两个输入参数的方法,并且返回一个结果

DoubleFunction<R>:代表接受一个double值参数的方法,并且返回结果

DoubleToIntFunction:接受一个double类型输入,返回一个int类型结果。

DoubleToLongFunction:接受一个double类型输入,返回一个long类型结果

IntFunction<R>:接受一个int类型输入参数,返回一个结果 。

IntToDoubleFunction:接受一个int类型输入,返回一个double类型结果 。

IntToLongFunction:接受一个int类型输入,返回一个long类型结果。

LongFunction<R>: 接受一个long类型输入参数,返回一个结果。

LongToDoubleFunction: 接受一个long类型输入,返回一个double类型结果。

LongToIntFunction:接受一个long类型输入,返回一个int类型结果。

ToDoubleBiFunction<T,U>:接受两个输入参数,返回一个double类型结果

ToDoubleFunction<T>:接受一个输入参数,返回一个double类型结果

ToIntBiFunction<T,U>:接受两个输入参数,返回一个int类型结果。

ToIntFunction<T>:接受一个输入参数,返回一个int类型结果。

ToLongBiFunction<T,U>:接受两个输入参数,返回一个long类型结果。

ToLongFunction<T>:接受一个输入参数,返回一个long类型结果。
### Java `BiFunction` 接口概述 `BiFunction<T, U, R>` 是一个函数式接口,接受两个参数并返回一个结果。此接口中的抽象方法是 `apply(T t, U u)`,它用于执行操作并将结果作为输出提供[^1]。 ### 使用场景与实例说明 #### 实例一:基本数值计算 当需要基于两个输入参数进行某种形式的数据处理时,可以使用 `BiFunction` 来简化代码逻辑: ```java import java.util.function.BiFunction; public class Example { public static void main(String[] args) { BiFunction<Integer, Integer, Double> divide = (a, b) -> a.doubleValue() / b; System.out.println(divide.apply(10, 2)); // 输出5.0 } } ``` 上述例子展示了如何利用 `BiFunction` 对整数除法运算进行了封装,并通过 lambda 表达式的简洁方式实现了功能。 #### 实例二:字符串拼接 除了简单的算术运算外,还可以应用于更复杂的业务逻辑中,比如连接两个字符串: ```java import java.util.function.BiFunction; public class StringConcatExample { public static void main(String[] args) { BiFunction<String, String, String> concatStrings = String::concat; System.out.println(concatStrings.apply("Hello", "World")); // 输出 HelloWorld } } ``` 这里采用静态方法引用的方式指定了具体的实现细节,使得代码更加直观易懂。 #### 实例三:结合Stream API 进行复杂转换 在某些情况下可能还需要与其他API组合起来完成特定的任务,例如下面的例子就展示了一个将列表内元素两两相加的过程: ```java import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; import java.util.function.BiFunction; public class StreamWithBiFunction { public static void main(String[] args) { List<Integer> numbers = Arrays.asList(1, 2, 3); BiFunction<List<Integer>, List<Integer>, List<Integer>> addLists = (list1, list2) -> IntStream.range(0, Math.min(list1.size(), list2.size())) .mapToObj(i -> list1.get(i) + list2.get(i)) .collect(Collectors.toList()); System.out.println(addLists.apply(numbers, Arrays.asList(4, 5, 6))); // 输出 [5, 7, 9] } } ``` 在这个案例里不仅体现了 `BiFunction` 的灵活性,同时也证明了其能够很好地融入到现代Java开发模式当中去。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Archie_java

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值