JUC并发编程学习(十二)-四大函数式接口(1)

简介: JUC并发编程学习(十二)-四大函数式接口(1)

函数式接口

有且只有一个抽象方法的接口。

函数式接口,即适用于函数式编程场景的接口。而java中的函数式编程体现就是Lambda,所以函数式接口 就是可以适用于Lambda使用的接口。只要确保接口中有且只有一个抽象方法,而java中的Lambda才能顺利进行推导。

格式

只要确保接口中有且仅有一个抽象方法即可:

修饰符 interface 接口名称{
        public abstract 返回值类型  方法名称(可选参数信息);
        //其他非抽象方法内容
}

由于接口当中抽象方法 public abstract是可以被省略的,所以定义一个函数式接口很简单:

public interface MyFunctionInterface{
    void myMethod();
}

@FunctionalInterface注解

与 @Override 注解的作用类似,Java 8中专门为函数式接口引入了一个新的注解: @FunctionalInterface 。该注解可用于一个接口的定义上:

@FunctionalInterface
public interface MyFunctionInterface{
    void myMethod();
}

一旦使用该注解来定义接口,编译器将会被强制检查该接口是否确实有且仅有一个抽象方法,否则将会报错。需要注意的是,即使不使用此注解,只要满足函数式接口的定义,这仍然是一个函数式 接口,使用起来都一样。

自定义函数式接口

对于刚刚定义好的 MyFunctionalInterface 函数式接口,典型使用场景就是作为方法的参数:

package com.demo.java.study7;
/**
* @ClassName:
* @PackageName: com.demo.java.study7
* @author: youjp
* @create: 2020-02-15 13:48
* @description:
* @Version: 1.0
*/
public class Test {
    // 使用自定义的函数式接口作为方法参数
    public static void doSomeThing(MyFunctionInterface my){
        my.myMethod();// 调用自定义的函数式接口方法
    }
    public static void main(String[] args) {
        //调用函数式接口的方法
        doSomeThing(()->{
            System.out.println("Lambda执行了");
        });
    }
}

执行结果

20200401134307494.png

四大函数式接口

JDK提供了大量常用的函数式接口以丰富Lambda的典型使用场景,它们主要在 java.util.function 包中被提供。

20200401134307494.png

下面是最简单的几个接口及使用示例。

Supplier接口

java.util.function.Supplier 接口仅包含一个无参的方法: T get() 。用来获取一个泛型参数指定类型的对象数据。由于这是一个函数式接口,这也就意味着对应的Lambda表达式需要“对外提供”一个符合泛型类型的对象数据。

import java.util.function.Supplier;
/**
* @ClassName:
* @PackageName: com.demo.java.study7
* @author: youjp
* @create: 2020-02-15 14:51
* @description:
* @Version: 1.0
*/
public class Test5 {
    private static String getString(Supplier<String> supplier){
        return supplier.get();
    }
    public static void main(String[] args) {
        String msgA="hello";
        String msgB="world";
        String msgC="java";
        System.out.println(getString(()->msgA+msgB+msgC));
    }
}

练习:求数组元素最大值

使用 Supplier 接口作为方法参数类型,通过Lambda表达式求出int数组中的最大值。提示:接口的泛型请使用java.lang.Integer 类。

import java.util.function.Supplier;
/**
* @ClassName:
* @PackageName: com.demo.java.study7
* @author: youjp
* @create: 2020-02-15 14:55
* @description:
* @Version: 1.0
*/
public class Test6 {
    /**
     * @Param [supplier]
     * @return java.lang.Integer
     * @Author youjp
     * @Description //TODO 获取最大数
     * @throw
     **/
    private static Integer getMax(Supplier<Integer> supplier) {
        return supplier.get();
    }
    public static void main(String[] args) {
        int arr[] = {2, 3, 44, 55, 8};
        int maxNum = getMax(() -> {
            int max = 0;
            for (int i = 0; i < arr.length; i++) {
                if (arr[i] >= max) {
                    max = arr[i];
                }
            }
            return max;
        });
        System.out.println(maxNum);
    }
}

Consumer接口

java.util.function.Consumer 接口则正好与Supplier接口相反,它不是生产一个数据,而是消费一个数据,其数据类型由泛型决定。

抽象方法:accept

Consumer 接口中包含抽象方法 void accept(T t) ,意为消费一个指定泛型的数据。基本使用如:

import java.util.function.Consumer;
/**
* @ClassName:
* @PackageName: com.demo.java.study7
* @author: youjp
* @create: 2020-02-15 15:22
* @description:
* @Version: 1.0
*/
public class Test7 {
    private static void consumeString(Consumer<String> function){
            function.accept("hello");
    }
    public static void main(String[] args) {
        consumeString(s -> System.out.println(s));
    }
}

执行结果

20200401134307494.png

默认方法:andThen

如果一个方法的参数和返回值全都是 Consumer 类型,那么就可以实现效果:消费数据的时候,首先做一个操作,然后再做一个操作,实现组合。而这个方法就是 Consumer 接口中的default方法 andThen 。下面是JDK的源代码:

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

备注: java.util.Objects 的 requireNonNull 静态方法将会在参数为null时主动抛出

NullPointerException 异常。这省去了重复编写if语句和抛出空指针异常的麻烦。


要想实现组合,需要两个或多个Lambda表达式即可,而 andThen 的语义正是“一步接一步”操作。例如两个步骤组合的情况:

package com.demo.java.study7;
import java.util.function.Consumer;
/**
* @ClassName:
* @PackageName: com.demo.java.study7
* @author: youjp
* @create: 2020-02-15 15:22
* @description:
* @Version: 1.0
*/
public class Test7 {
    private static void consumeString(Consumer<String> one,Consumer<String> two){
        one.andThen(two).accept("Hello");
    }
    public static void main(String[] args) {
      //先转大写字母,再转小写字母
        consumeString(
                s -> System.out.println(s.toUpperCase()),
                s-> System.out.println(s.toLowerCase()));
    }
}

运行结果将会首先打印完全大写的HELLO,然后打印完全小写的hello。当然,通过链式写法可以实现更多步骤的组合。

20200401134307494.png

练习:格式化打印信息


下面的字符串数组当中存有多条信息,请按照格式“ 姓名:XX。性别:XX。 ”的格式将信息打印出来。要求将打印姓名的动作作为第一个 Consumer 接口的Lambda实例,将打印性别的动作作为第二个 Consumer 接口的Lambda实例,将两个 Consumer 接口按照顺序“拼接”到一起。

public static void main(String[] args) {
String[] array = { "迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男" };
}

解答

import java.util.function.Consumer;
/**
* @ClassName:
* @PackageName: com.demo.java.study7
* @author: youjp
* @create: 2020-02-15 15:53
* @description:
* @Version: 1.0
*/
public class Test8 {
    private static void consumeString(Consumer<String> one,Consumer<String> two,String[] array){
        for(String info:array){
            one.andThen(two).accept(info);
        }
    }
    public static void main(String[] args) {
        String[] array = { "迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男" };
        consumeString(
                s-> System.out.print("姓名:"+s.split(",")[0]),
                s -> System.out.println(" 性别:"+s.split(",")[1]+"。"),array);
    }
}

输出:

20200401134307494.png

相关文章
|
2月前
|
存储 安全 算法
Java并发基础:ConcurrentSkipListSet全面解析!
ConcurrentSkipListSet类在多线程环境下,它能够轻松应对大量的插入、删除和查找操作,同时保持数据的完整性和一致性,其内部基于跳表数据结构的实现,确保了即使在处理大规模数据时,也能具有出色的性能表现。
Java并发基础:ConcurrentSkipListSet全面解析!
|
8月前
|
算法 Java 调度
|
11月前
|
容灾 Java API
技术汇总:第二章:JUC
技术汇总:第二章:JUC
并发编程(十二)ForkJoin框架使用
并发编程(十二)ForkJoin框架使用
62 0
|
存储 Java
【Java技术指南】「并发编程专题」CompletionService框架基本使用和原理探究(基础篇)
【Java技术指南】「并发编程专题」CompletionService框架基本使用和原理探究(基础篇)
137 0
JUC并发编程学习(十二)-四大函数式接口(2)
JUC并发编程学习(十二)-四大函数式接口(2)
JUC并发编程学习(十二)-四大函数式接口(2)
|
分布式计算 大数据 Java
JUC并发编程学习(十四)-任务拆分ForkJoin详解
JUC并发编程学习(十四)-任务拆分ForkJoin详解
JUC并发编程学习(十四)-任务拆分ForkJoin详解
|
安全 Java
JUC并发编程学习(六)-集合类不安全
JUC并发编程学习(六)-集合类不安全
JUC并发编程学习(六)-集合类不安全
|
SQL 安全 Java
JUC并发编程学习(十八) -搞懂单例模式
JUC并发编程学习(十八) -搞懂单例模式
JUC并发编程学习(十八) -搞懂单例模式

热门文章

最新文章