[Java进阶学习][函数式编程][Java 8内置函数式接口]

[Java进阶学习][Java 8内置函数式接口]

简介

众所周知 Java8 最大的改变就是引入了Lambda表达式以实现函数式编程
Lambda到底是是什么 怎么用 此类文章多有赘述 此处不多谈
就像Java提供了内置的强大数据结构 Java集合框架JCF
或者Java提供的强大的并发编程工具 Java并发包JUC一样
Java8 为了更好的帮助开发者进行函数式编程
JDK内置了java.util.function函数式接口包

掌握JDK提供的这些函数式接口 可以更迅速 有效的帮助我们编写符合Lambda表达式
和函数式编程规则的代码

JUF 函数式接口

基于 Java 9
虽然JUF提供了 很多的函数式接口 但大体上也就是分为以下四种类型 其他的都是在这
四种类型上的扩充

接口参数返回值类型
FunctionTR函数式接口
PredicateTboolean断言式接口
ConsumerTvoid消费型接口
SuppliernoneT生产型接口

所有的函数式接口 顾名思义都是接口 需要传入具体的实现
这里是Lambda行为 才能运行

基本接口

Function 函数式接口

Fcuntion接口是对接受一个T类型参数,返回R类型的结果的方法的抽象,通过调用apply方法执行内容

源码

@FunctionalInterface
public interface Function<T, R> {
    R apply(T t);

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

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

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

从源码上看 Function接口还是比较简单的
只有一个主方法 所以类上加注了@FunctionalInterface函数式接口注解

R apply(T t);

此方法接收一个泛型参数T t 进行处理后 返回泛型结果R
Function接口接收一个Lambda行为,apply方法应用此行为进行处理和数据转换
还可以配合默认方法compose,andThen进行链式调用 进行多级处理
实现更复杂和丰富的功能

使用方法示例

先编写一个以Function为参数 可以传入函数式行为的方法

   public int compute(int a, Function<Integer, Integer> function) {
        int result = function.apply(a);
        return result;
    }

然后调用此方法 传入具体的lambda表达式

     System.out.println(this.compute(1,x->x+7));
     System.out.println(this.compute(1,x->x-1));
     System.out.println(this.compute(2,x->x*x));

结果

8
0
4

apply()方法应用了传入的行为 进行了运算 并返回处理结果

上面的是进行计算 还可以指定不同的T,R类型 比如数据类型转换

public static Integer convert(String str, Function<String, Integer> function) {
    return function.apply(str);
}

转换操作

Integer value = convert("28", x -> Integer.parseInt(x));

返回了一个整型 28

链式调用

使用Function的默认方法 可以链式的调用一系列的Function接口的行为

写两个多重计算的方法
第一个使用了默认方法

default < V> Function< V, R> compose(Function< ? super V, ? extends T> before)

此默认方法会先处理传入的before行为 再运行之后的操作
所以 multiCompute泛型方法中 会先处理before中的行为 再应用after的行为

     public <T> T multiCompute(T a,Function<T,T> before,Function<T,T> after) {
      return after.compose(before).apply(a);
    }

第二个使用了默认方法

default < V> Function< T, V> andThen(Function< ? super R, ? extends V> after)

此默认方法会一般被其他函数式行为调用 执行时 先执行调用者自身的行为内容
再执行andThen方法传入的行为内容
所以multiCompute2泛型方法 会先运行before的行为 再执行after的行为

    public <T> T multiCompute2(T a,Function<T,T> before,Function<T,T> after) {
        return before.andThen(after).apply(a);
    }

测试

        int result=this.multiCompute(5,x->x*2,x->x*x);
        System.out.println("result="+result);
        int result2=this.multiCompute2(5,x->x*2,x->x*x);
        System.out.println("result2="+result2);

结果

result=50
result2=100

另外Function接口还有一个静态方法identity() 返回元素本身

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

这个方法主要用在不需要对数据处理的行为中 等同于lambda表达式 x->x

以下代码时等价的

        list.stream().map(x->x);
        list.stream().map(Function.identity());

Predicate 断言式接口

源码

@FunctionalInterface
public interface Predicate<T> {
    boolean test(T t);

    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> {
            return this.test(t) && other.test(t);
        };
    }

    default Predicate<T> negate() {
        return (t) -> {
            return !this.test(t);
        };
    }

    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> {
            return this.test(t) || other.test(t);
        };
    }

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

和Function接口 一样Predicate断言式接口作为一个函数是接口
只有一个方法

boolean test(T t);

一个断言式接口接受一个lambda包含判断的行为 调用test方法 返回判断结果的逻辑真假值

编写一个断言方法

    public <T> List<T> filter(List<T> list,Predicate<T> predicate) {

        List<T> result=new ArrayList<>(list.size());

        list.forEach(x->{

            if(predicate.test(x))
                result.add(x);
        });
        return result;
    }

调用

   List<String> list=Stream
        .of("apple","banana","coconut","Damson","Filbert","Lemon",)
        .collect(Collectors.toList());

        List newList=this.filter(list,x->x.startsWith("a"));
        newList.forEach(System.out::println);
        //结果 apple

一般此接口常用在Stream流中调用 用以过滤流内容

   List<String> list=Stream.of("apple","banana","coconut","Damson","Filbert","Lemon")
                .collect(Collectors.toList());

   list.stream().filter(x->x.startsWith("a")).forEach(System.out::println);
   //输出结果 apple

Predicate接口还提供了 三个默认方法 and(),or(),negate()
顾名思义 这三个默认方法 是 和 与 否定 用于生成一组 组合的判断行为

组合判断

主代码

  List newList = this.filterCombination(list, x -> x.contains("e"), x -> x.length() < 6);
        newList.forEach(System.out::println);

和断言

    public <T> List<T> filterCombination(List<T> list,Predicate<T> first,Predicate<T> last) {

        List<T> result=new ArrayList<>(list.size());

        list.forEach(x->{

            if(first.and(last).test(x))
                result.add(x);
        });
        return result;
    }

调用结果

apple
Lemon

上面的判断后继续调用negate()方法 即可获得相反的断言内容

否断言

    public <T> List<T> filterCombination(List<T> list,Predicate<T> first,Predicate<T> last) {
        List<T> result=new ArrayList<>(list.size());
        list.forEach(x->{
            if(first.and(last).negate().test(x))
                result.add(x);
        });
        return result;
    }

结果 刚好和上面的和断言结果是反的

banana
coconut
Damson
Filbert

或断言

主代码

  List<String> list=Stream.of("apple","banana","coconut","Damson","Filbert","Lemon").collect(Collectors.toList());

        List newList = this.filterCombination(list, x -> x.contains("a"), x -> x.length() < 6);
        newList.forEach(System.out::println);

断言

 public <T> List<T> filterCombination(List<T> list,Predicate<T> first,Predicate<T> last) {
        List<T> result=new ArrayList<>(list.size());
        list.forEach(x->{
            if(first.or(last).test(x))
                result.add(x);
        });
        return result;
    }

结果

apple
banana
Damson
Lemon

和Function接口一样 Predicat接口也有一个静态方法

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

此方法 返回了一个Predicate断言行为 行为内容是使用的targetRef值相等判断
内部调用了targetRef的equals方法 如果targetRef重写了equals方法 则调用重写后的方法
否则调用Object的equals方法

比较不容易懂 结合示例看

String str="test";
Predicate.isEqual(str).test("test")

此处即Predicate接口调用了str的equals方法 判断其是否和”test”字符串相等
自然是返回true

Consumer 消费型接口

Consumer 翻译过来就是消费者,顾名思义,该接口对应的方法类型为接收一个参数,没有返回值。也就是说此接口将传入参数’消费掉了’,一般来说使用Consumer接口往往伴随着一些期望状态的改变或者事件的发生,例如最典型的forEach就是使用的Consumer接口,虽然没有任何的返回值,但是对传入的参数进行了处理。

public interface Consumer<T> {

    void accept(T t);

    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}

源码很简单 一个接受行为并处理的accept()方法

void accept(T t);

另外还有一个默认方法andThen()
显然是用于组合链式调用

示例

List<String> list=Stream.of("apple","banana","coconut","Damson","Filbert","Lemon").collect(Collectors.toList());

        this.Consuming(list,x-> System.out.println(x));

运行结果

apple
banana
coconut
Damson
Filbert
Lemon

这个接口最多的应用在Stream流或者集合列表中的forEach方法
进行数据处理中 一般不用自己写

Supplier 生产型接口

生产型接口 和其他几个函数是接口都不同
相比消费型接口的只接受 不返回值
此接口无输入值 但有返回值
类似生产者 所以命名为生产型接口

@FunctionalInterface
public interface Supplier<T> {

    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();
}

通常用于配合其他Lambda方法使用

比如配合Optional使用

 Optional.ofNullable(list).orElseGet(()->new ArrayList<>());

Optional的orElseGet方法接受一个Supplier参数

orElseGet(Supplier< ? extends T> supplier)

此处以lambda的形式新构造了一个空的ArrayList

拓展接口

基础接口的返回值限定扩展接口

类似Stream有四种 基本Stream 和限定入参类型的IntStream DoubleStream LongStream

所有的函数是接口 除了基本的T泛型接口外 也有三种Int,Long,Double扩展接口
这三种接口和基本泛型接口不同的地方就是 限定了入参类型甚至限定了返回值类型

Function扩展

Function
|—–IntFunction
|—–LongFunction
|—–DoubleFunction
|—–ToIntFunction
|—–ToLongFunction
|—–ToDoubleFunction
|—–IntToDoubleFunction
|—–IntToLongFunction.
|—–LongToIntFunction
|—–LongToDoubleFunction
|—–DoubleToIntFunction
|—–DoubleToLongFunction

看Function接口源码

public interface Function<T, R> {

    R apply(T t);

    ……
}

有两个泛型声明 T,R
apply方法 入参T 返回R
那么其拓展方法就很容易理解了

  • ?Function就是限定入参T为?类型
    例如IntFunction
public interface IntFunction<R> {

    R apply(int value);
}

限定入参为 int型 返回类型为泛型R型

  • To?Function就是限定返回类型为?类型
    例如ToLongFunction
public interface ToLongFunction<T> {
    long applyAsLong(T t);
}

入参为泛型T不变 返回值限定为long型

  • ?To*Function就是限定入参T为?类型 返回值为*类型
    例如DoubleToIntFunction
public interface DoubleToIntFunction {
    int applyAsInt(double d);
}

限定入参为 double型 返回类型为int型

Prdicate扩展

Predicate
|—–IntPredicate
|—–LongPredicate
|—–DoublePredicate

和Function琳琅满目的扩展不同 Predicate接口因为返回值已经限定为boolean类型
所以只有三种扩展接口
分别限定了入参类型为Int Long Double

例 IntFunction

public interface IntPredicate {

    boolean test(int value);
}
Consumer扩展

Consumer
|—–IntConsumer
|—–LongConsumer
|—–DoubleConsumer

类似Predicate接口 因为返回值已经限定为void类型
所以只有三种扩展接口
分别限定了入参类型为Int Long Double

例如IntConsumer

public interface IntConsumer {

    void accept(int value);
}

限定入参为int型

但是Consumer有三个外延的接口
ObjIntConsumer
ObjDoubleConsumer
ObjLongConsumer

这三个接口可以同时接收两个参数 一个T类型 另一个则为Obj?Consumer的?类型
例如ObjDoubleConsumer

@FunctionalInterface
public interface ObjDoubleConsumer<T> {
    void accept(T t, double value);
}
Supplier 扩展

Supplier
|—–IntSupplier
|—–LongSupplier
|—–DoubleSupplier

类似Predicate接口 因为入参已经限定为void类型
所以只有三种扩展接口
分别限定了返回类型为Int Long Double

例如 DoubleSupplier

@FunctionalInterface
public interface DoubleSupplier {
    double getAsDouble();
}

返回值类型已经限定为double类型

基础接口的入参二元扩展接口

有些基础接口的扩展接口在原有的参数之外再接收一个参数
此类接口的所有名字前面都是附加上Bi,是Binary的缩写 意思是二元的 双重的
例如BiPredicate,BiFcuntion等等,而由于java没有多返回值的设定,所以Bi指的都是参数为两个

接口名
BiConsumer
BiFunction
BiPredicate
ToDoubleBiFunction
ToIntBiFunction
ToLongBiFunction

可以看到 这里面扩展接口是以Function为主
以BiFunction为例

源码

public interface BiFunction<T, U, R> {

    R apply(T t, U u);
}

和Function不同的就是入参变为两个 T t,U u

代码示例

    BiFunction<Integer,Integer,Integer> biFunction=(x,y)->(x+y+y);
    int res=biFunction.apply(1,2);
    System.out.println(res);
   //结果5

BiPredicate 示例

        BiPredicate<Integer,Integer> biPredicate=(i,j)->(i+j>2);

        boolean result=biPredicate.test(1,2);
        System.out.println(result);// true
操作型扩展

此类接口只有2个

接口名描述
UnaryOperator一元操作符
BinaryOperator二元操作符

看UnaryOperator的源码

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

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

很明显可以看出是继承自Function< T, R>接口
只不过将返回值也统一化为T 即入参和返回值都是T类型
这类接口属于Function接口的简写,他们只有一个泛型参数
意思是Funtion的参数与返回值类型相同,一般多用于操作计算
比如 计算 a + b的BiFcuntion如果限制入参和返回值为Integer的话 则是这样的
BiFunction<Integer,Integer,Integer>
前2个泛型代表参数,最后一个代表返回值 有些过于繁琐
这个时候就可以用BinaryOperator<Integer>来代替了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值