Java杂项系列--函数式接口(java.util.function包)

其他网址

JDK8函数式接口Function、Consumer、Predicate、Supplier_酒肉猿-CSDN博客
Java8中Function函数式接口详解及使用_Life is for sharing的博客-CSDN博客
JAVA8的java.util.function包讲解 (方便的函数式编程)_我是guyue,guyue就是我O(∩_∩)O-CSDN博客

《JAVA开发实战经典 第2版》=> 16.10 内建函数式接口

简介

JDK1.8函数接口变化

JDK1.8新增加的函数接口:

  • java.util.function
  • java.util.stream

JDK1.8之前已有的函数式接口:

  • java.lang.Runnable
  • java.util.concurrent.Callable
  • java.security.PrivilegedAction
  • java.util.Comparator
  • java.io.FileFilter
  • java.nio.file.PathMatcher
  • java.lang.reflect.InvocationHandler
  • java.beans.PropertyChangeListener
  • java.awt.event.ActionListener
  • javax.swing.event.ChangeListener

常用接口

接口定义

说明

作用

Function< T, R >

功能型接口

接收T对象,返回R对象。

Consumer< T >

消费型接口

接收T对象,不返回值

Supplier< T >

供给型接口

不接收值,返回T对象

Predicate< T >

断言型接口

接收T对象并返回boolean

不常用接口

接口定义

描述

UnaryOperator< T >

接收T对象,返回T对象

BiConsumer<T, U>

接收T对象和U对象,不返回值

BiPredicate<T, U>

接收T对象和U对象,返回boolean

BiFunction<T, U, R>

接收T对象和U对象,返回R对象

BinaryOperator< T >

接收两个T对象,返回T对象

Function< T, R >  功能型

简介

概述

作用

实现一个”一元函数“,即传入一个值经过函数的计算返回另一个值。

使用场景

V HashMap.computeIfAbsent(K , Function<K, V>) // 简化代码,如果指定的键尚未与值关联或与null关联,使用函数返回值替换。
<R> Stream<R> map(Function<? super T, ? extends R> mapper); // 转换流

设计思想

一元函数的思想,将转换逻辑提取出来,解耦合

定义

package java.util.function;
import java.util.Objects;

@FunctionalInterface
public interface Function<T, R> {
    // 接受输入参数,对输入执行所需操作后  返回一个结果。
    R apply(T t);

    // 返回一个 先执行before函数对象apply方法,再执行当前函数对象apply方法的 函数对象。
    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
       Objects.requireNonNull(before);
       return (V v) -> apply(before.apply(v));
    }

    // 返回一个 先执行当前函数对象apply方法, 再执行after函数对象apply方法的 函数对象。
    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }   

    // 返回一个执行了apply()方法之后只会返回输入参数的函数对象。
    static <T> Function<T, T> identity() {
        return t -> t;
    } 
}

所有接口

接口定义

描述

Function< T, R >

接收T对象,返回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结果的函数

实例

apply

Function<String, String> function = a -> a + " Jack!";
System.out.println(function.apply("Hello")); // Hello Jack!

compose

Function<String, String> function = a -> a + " Jack!";
Function<String, String> function1 = a -> a + " Bob!";
String greet = function.compose(function1).apply("Hello");
System.out.println(greet); // Hello Bob! Jack!

andThen

Function<String, String> function = a -> a + " Jack!";
Function<String, String> function1 = a -> a + " Bob!";
String greet = function.andThen(function1).apply("Hello");
System.out.println(greet); // Hello Jack! Bob!

Consumer< T > 消费型

简介

概述

作用

消费某个对象

使用场景

Iterable接口的forEach方法需要传入Consumer,大部分集合类都实现了该接口,用于返回Iterator对象进行迭代。

设计思想

开发者调用ArrayList.forEach时,一般希望自定义遍历的消费逻辑,比如:输出日志或者运算处理等。
处理逻辑留给使用者,使用灵活多变。
多变的逻辑能够封装成一个类(实现Consumer接口),将逻辑提取出来。

定义

package java.util.function;

import java.util.Objects;

@FunctionalInterface
public interface Consumer<T> {
    //提供一个T类型的输入参数,不返回执行结果
    void accept(T t);

    //返回一个组合函数,after将会在该函数执行之后应用
    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}

所有接口

接口

描述

Consumer<T>

提供一个T类型的输入参数,不返回执行结果

BiConsumer<T,U>

提供两个自定义类型的输入参数,不返回执行结果

DoubleConsumer

表示接受单个double值参数,但不返回结果的操作

IntConsumer

表示接受单个int值参数,但不返回结果的操作

LongConsumer

表示接受单个long值参数,但不返回结果的操作

ObjDoubleConsumer<T>

表示接受object值和double值,但是不返回任何操作结果

ObjIntConsumer<T>

表示接受object值和int值,但是不返回任何操作结果

ObjLongConsumer<T>

表示接受object值和long值,但是不返回任何操作结果

实例

accept

StringBuilder sb = new StringBuilder("Hello ");
Consumer<StringBuilder> consumer = (str) -> str.append("Jack!");
consumer.accept(sb);
System.out.println(sb.toString());	// Hello Jack!

andThen

StringBuilder sb = new StringBuilder("Hello ");
Consumer<StringBuilder> consumer = (str) -> str.append("Jack!");
Consumer<StringBuilder> consumer1 = (str) -> str.append(" Bob!");
consumer.andThen(consumer1).accept(sb);
System.out.println(sb.toString());	// Hello Jack! Bob!

Supplier< T >  供给型

简介

概述

作用

创建一个对象(工厂类)

使用场景

Optional.orElseGet(Supplier<? extends T>):当this对象为null,就通过传入supplier创建一个T返回。

设计思想

封装工厂创建对象的逻辑

定义

package java.util.function;

@FunctionalInterface
public interface Supplier<T> {
    //获取结果值
    T get();
}

不常用接口

接口

描述

Supplier<T>

不提供输入参数,但是返回结果的函数

BooleanSupplier

不提供输入参数,但是返回boolean结果的函数

DoubleSupplier

不提供输入参数,但是返回double结果的函数

IntSupplier

不提供输入参数,但是返回int结果的函数

LongSupplier

不提供输入参数,但是返回long结果的函数

实例

get

Supplier<String> supplier = () -> "Hello Jack!";
System.out.println(supplier.get()); // Hello Jack!

Predicate< T > 断言型接口

简介

概述

作用

  • 判断对象是否符合某个条件

使用场景

  • ​ArrayList的removeIf(Predicate):删除符合条件的元素
  • ​如果条件硬编码在ArrayList中,它将提供无数的实现,但是如果让调用者传入条件,这样ArrayList就可以从复杂和无法猜测的业务中解放出来。

设计思想

  • 提取条件,让条件从处理逻辑脱离出来,解耦合

定义

package java.util.function;

import java.util.Objects;

@FunctionalInterface
public interface Predicate<T> {

    // 根据给定的参数进行判断
    boolean test(T t);

    // 返回一个组合判断,将other以短路与的方式加入到函数的判断中
    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }

    // 将函数的判断取反
    default Predicate<T> negate() {
        return (t) -> !test(t);
    }

    // 返回一个组合判断,将other以短路或的方式加入到函数的判断中
    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }

    
    static <T> Predicate<T> isEqual(Object targetRef) {
        return (null == targetRef)
                ? Objects::isNull
                : object -> targetRef.equals(object);
    }
}

所有接口

接口

描述

Predicate<T>

对给定的输入参数执行操作,返回一个boolean类型的结果(布尔值函数)

BiPredicate<T,U>

对给定的两个输入参数执行操作,返回一个boolean类型的结果(布尔值函数)

DoublePredicate

对给定的double参数执行操作,返回一个boolean类型的结果(布尔值函数)

IntPredicate

对给定的int输入参数执行操作,返回一个boolean类型的结果(布尔值函数)

LongPredicate

对给定的long参数执行操作,返回一个boolean类型的结果(布尔值函数)

实例

test

Predicate<Integer> predicate = number -> number != 0;
System.out.println(predicate.test(10));    //true

and

Predicate<Integer> predicate = number -> number != 0;
predicate = predicate.and(number -> number >= 10);
System.out.println(predicate.test(10));    //true

or

Predicate<Integer> predicate = number -> number != 0;
predicate = predicate.or(number -> number != 10);
System.out.println(predicate.test(10));    //true

原理

其他网址

【Java8】Function 讲解_sowhat-CSDN博客

apply

        首先我们已经知道了Function是一个泛型类,其中定义了两个泛型参数T和R,在Function中,T代表输入参数,R代表返回的结果。也许你很好奇,为什么跟别的java源码不一样,Function 的源码中并没有具体的逻辑呢?

        其实这很容易理解,Function 就是一个函数,其作用类似于数学中函数的定义:y = f(x) ,(x,y)跟<T,R>的作用几乎一致。所以Function中没有具体的操作,具体的操作需要我们指定,apply具体返回的结果取决于传入的lambda表达式。

举例:

Function<Integer, Integer> test = i -> i + 1;
System.out.println(test.apply(5));

        用lambda表达式定义了一个行为使得i自增1,我们使用参数5执行apply,最后返回6。这跟我们以前看待Java的眼光已经不同了,在函数式编程之前我们定义一组操作首先想到的是定义一个方法,然后指定传入参数,返回我们需要的结果。函数式编程的思想是先不去考虑具体的行为,而是先去考虑参数,具体的方法我们可以后续再设置。

再举个例子:

public void test() {
    Function<Integer, Integer> test1 = i -> i + 1;
    Function<Integer, Integer> test2 = i -> i * i;
    /* print:6 */
    System.out.println(calculate(test1, 5));
    /* print:25 */
    System.out.println(calculate(test2, 5));
}

public static Integer calculate(Function<Integer, Integer> test, Integer number) {
    return test.apply(number);
}

        通过传入不同的Function,实现了在同一个方法中实现不同的操作。在实际开发中这样可以大大减少很多重复的代码,比如我在实际项目中有个新增用户的功能,但是用户分为VIP和普通用户,且有两种不同的新增逻辑。那么此时我们就可以先写两种不同的逻辑。除此之外,这样还让逻辑与数据分离开来,我们可以实现逻辑的复用。

        当然实际开发中的逻辑可能很复杂,比如两个方法F1,F2都需要操作AB,但是F1需要A->B,F2方法需要B->A。这样的我们用刚才的方法也可以实现,源码如下:

public void test() {
    Function<Integer, Integer> A = i -> i + 1;
    Function<Integer, Integer> B = i -> i * i;
    /* F1:36 */
    System.out.println("F1:" + B.apply(A.apply(5)));
    /* F2:26 */
    System.out.println("F2:" + A.apply(B.apply(5)));
}

compose和andThen

如上边例子所示,假如我们F1,F2需要四个逻辑ABCD,那我们还这样写就会变得很麻烦了。compose和andThen可以解决我们的问题。

先看compose的源码:compose接收一个Function参数,返回时先用传入的逻辑执行apply,然后使用当前Function的apply。如下所示

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

andThen跟compose正相反,先执行当前的逻辑,再执行传入的逻辑:

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

这样说可能不够直观,我可以换个说法:compose等价于B.apply(A.apply(5)),而andThen等价于A.apply(B.apply(5))。

Function<Integer, Integer> A = i -> i + 1;
Function<Integer, Integer> B = i -> i * i;
/* F1:36 */
System.out.println("F1:" + B.apply(A.apply(5)));
/* F1:36 */
System.out.println("F1:" + B.compose(A).apply(5));
/* F2:26 */
System.out.println("F2:" + A.apply(B.apply(5)));
/* F2:26 */
System.out.println("F2:" + B.andThen(A).apply(5));

 

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页