【java8新特性】02:常见的函数式接口

Jdk8提供的函数式接口都在java.util.function包下,Jdk8的函数式类型的接口都有@FunctionInterface注解所标注,但实际上即使没有该注解标注的有且只有一个抽象方法的接口,都可以算是函数式接口。
在JDK8中内置的四大核心函数式接口如下:
函数式接口接口类型参数类型返回类型作用Stream流中的应用场景Consumer 消费型接口T void 对类型为T的对象进行操作,包含方法为accpet(T t)如forEach、peek等方法的函数式接口都是Consumer类型Supplier 供给型接口无T 返回类型为T的对象,包含方法为T get()如collect等方法的某些方法重载就是用的Supplier类型Function 函数型接口T R 对类型为T的对象进行操作,返回结果为 R类型的对象,包含方法为R apply(T t)如map,flatMap等方法的函数式接口都是Function类型Predicate 断言型接口T boolean 确定类型为T的对象是否满足约束,并返回约束结果,包含方法为boolean test(T t)如filter等方法的函数式接口都是Predicate类型

Consumer

Consumer

基本使用

public class Main {
    /**
     * Consumer
     *     消费型接口:顾名思义主要用于消费参数,不反馈调用环境(没有返回值)
     *     accept: 抽象方法实现,用于调用方法。
     *     andThen: 默认实现方法,内部允许我们链式调用
     */
    public static void main(String[] args) {
        // 给定字符串转为大写并输出到控制台,匿名内部类的方式实现
        Consumer con1 = new Consumer() {
            @Override
            public void accept(String str) {
                System.out.println("通过匿名内部类的方式:"+ str.toUpperCase());
            }
        };
        // 执行该方法的时候,我们传入了给定参数字符串,它会去执行我们上述实现的accept方法并传入参数,最后执行我们给定的代码逻辑
        con1.accept("abc");
        // 给定字符串转为大写并输出到控制台,通过Lambda表达式实现
        Consumer con2 = (text)-> System.out.println("通过Lambda表达式的方式:"+ text.toUpperCase());
        con2.accept("goods");
        /**
         * 最终结果:
         * 通过匿名内部类的方式:ABC
         * 通过Lambda表达式的方式:GOOD
         * 使用lambda表达式,我们只需要记住参数列表和执行逻辑即可,其他的我们无需关注。
         */
    }}

学习案例

java;gutter:true; public class Main {</p> <pre><code>/** * Consumer * 消费型接口:顾名思义主要用于消费参数,不反馈调用环境(没有返回值) * accept: 抽象方法实现,用于调用方法。 * andThen: 默认实现方法,内部允许我们链式调用 */ public static void main(String[] args) { // 1.我们需要将集合进行排序后在输出到控制台 Consumer con1 = list-> { System.out.println("排序前的集合:"+ list); Collections.sort(list); System.out.println("排序后的集合:"+list); }; con1.accept(Arrays.asList(1,5,3,2,9,6,7)); /** * 最终结果: * 排序前的集合:[1, 5, 3, 2, 9, 6, 7] * 排序后的集合:[1, 2, 3, 5, 6, 7, 9] */ // 上面执行逻辑实现分两步,第一步需要获取到给定集合进行排序,第二个则是输出排序后的集合 // 如果以上两个步骤分别用两个consumer也可以实现,我们可以定义一个方法接收两个consumer进行操作 accept(Arrays.asList(1,5,3,2,9,6,7),list-> { System.out.println("andThen链式调用前集合:"+list); Collections.sort(list); },list-> System.out.println("andThen链式调用后集合:"+ list)); /** * 最终结果: * andThen链式调用前集合:[1, 5, 3, 2, 9, 6, 7] * andThen链式调用后集合:[1, 2, 3, 5, 6, 7, 9] */ // 如果consumer参数多个的话,我们可以直接在Lambda表达式进行链式调用,不费那劲定义方法了 Consumer con2 = ((Consumer) list -> { System.out.println("lambda表达式的链式调用前集合:" + list); Collections.sort(list); }).andThen(list -> System.out.println("lambda表达式的链式调用后集合:"+list)); // 需要注意的是:要使用这种方式,第一个consumer要进行链式调用必须要强行指定为(Consumer)类型,后续的接口才能够调用方法 con2.accept(Arrays.asList(1,53,31,25,99,62,17)); /** * 最终结果: * lambda表达式的链式调用前集合:[1, 53, 31, 25, 99, 62, 17] * lambda表达式的链式调用后集合:[1, 17, 25, 31, 53, 62, 99] */ } public static void accept(List list,Consumer con1,Consumer con2){ // 链式调用时会优先执行左边的接口实现,依次往右执行 我们的需求是先排序后输出,第一个Consumer是排序,第二个是输出。 con1.andThen(con2).accept(list); } </code></pre> <p>}</p> <pre><code> #### 总结 > 1.函数式接口的本质实际上就是将函数以参数的形式进行传递 2.Consumer是消费型的函数式接口,通常用于数据内部处理,没有返回值 3.除了Consumer之外,还有各种消费型的函数式接口,还有IntConsumer、LongConsumer等、如果需要传递两个参数则可以使用BIFunction、也可以根据自身需求进行自定义。 ### Supplier > Consumer #### 基本使用 </code></pre> <p>public class Main{ /<strong> * 供给型函数式接口顾名思义就是顾名思义就是供给数据给调用环境,不接收参数传递 * T get() : 返回泛型T类型的参数到调用环境 */ public static void main(String[] args) { // 返回一个0-100间的随机数 Supplier sup1 = new Supplier() { @Override public Integer get() { int res = new Random().nextInt(100); System.out.println("通过匿名内部类的方式获取到的随机数:"+ res); return res; } }; // 执行该方法的时候,它会去执行我们上述实现的get方法。 sup1.get(); // 通过lambda表达式的方式进行实现 Supplier sup2 = ()-> { int res = new Random().nextInt(100); System.out.println("通过lambda表达式的方式获取到的随机数:"+ res); return res;}; sup2.get(); /</strong> * 最终结果: * 通过匿名内部类的方式获取到的随机数:28 * 通过lambda表达式的方式获取到的随机数:62 * 使用lambda表达式,我们只需要记住参数列表和执行逻辑即可,其他的我们无需关注。 */ } }</p> <pre><code> #### 学习案例 </code></pre> <p>public class Main{</p> <pre><code>public static Map redis = new HashMap(); /** * 供给型函数式接口顾名思义就是顾名思义就是供给数据给调用环境,不接收参数传递 * T get() : 返回泛型T类型的参数到调用环境 */ public static void main(String[] args) { // 1.(模拟)查询某个Key在redis中有没有缓存,缓存没有则从数据库取完存入redis再返回,有的话则直接返回 String val = getCache("title"); String val2 = getCache("title"); String val3 = getCache("title"); /** * 最终结果: 从数据库中获取:我是标题 从缓存中获取:我是标题 从缓存中获取:我是标题 */ // 可以看到经过第一次后续都是直接从缓存中取出的数据 } public static String getCache(String key){ String val = redis.get(key); if(Objects.isNull(val)){ // 获取数据库的数据 val = getDbVal(() -> "我是标题"); System.out.println("从数据库中获取:"+val); redis.put(key,val); return val; } System.out.println("从缓存中获取:"+val); return val; } public static String getDbVal(Supplier supplier){ return supplier.get(); } </code></pre> <p>}</p> <pre><code> #### 总结 > 1.函数式接口的本质实际上就是将函数以参数的形式进行传递 2.Supplier是供给型的函数式接口,通常用于构建某个对象处理后返回调用环境 3.除了Supplier之外,还有各种供给型的函数式接口,还有BooleanSupplier、IntSupplier等。 ### Function > Function #### 基本使用 ;gutter:true;
public class Main{

/**
* Function函数型的函数式接口,泛型T是参数、泛型R则是返回值、主要应用场景做数据类型转换等。
* R apply(T t): 抽象方法实现,用于调用方法并返回泛型R
* Function andThen: 默认实现方法,内部允许我们链式调用,与其他的andThen原理一致。
* Function compose: 默认实现方法,内部允许我们链式调用,调用方式与andThen一样,但执行顺序不一样,compose是先执行compose中的函数接口,再执行左边调用的函数接口,依次往左
* Function identity():返回当前执行的方法,从源码中我们也可以看到它返回的是当前的t
*/
public static void main(String[] args) {

// 传入给定字符串,返回转换后的Integer类型
Function fun1 = new Function() {
@Override
public Integer apply(String s) {
Integer convert = Integer.valueOf(s);
System.out.println("通过匿名内部类的方式获取到的值:"+ convert +",数据类型是否为Integer?结果:" + (convert instanceof Integer));
return convert;
}
};
// 执行该方法的时候,它会去执行我们上述实现的apply方法。
fun1.apply("10086");
// 通过lambda表达式的方式进行实现
Function fun2 = s->{
Integer convert = Integer.valueOf(s);
System.out.println("通过lambda表达式的方式获取到的值:"+ convert +",数据类型是否为Integer?结果:" + (convert instanceof Integer));
return convert;
};
fun2.apply("10000");
/**
* 通过匿名内部类的方式获取到的值:10086,数据类型是否为Integer?结果:true
* 通过lambda表达式的方式获取到的值:10000,数据类型是否为Integer?结果:true
* 使用lambda表达式,我们只需要记住参数列表和执行逻辑即可,其他的我们无需关注。
*/
// 我们继续对Function的API做一些理解和补充,毕竟这玩意在工作中经常会用上
// andThen 我们都知道常用于链式调用的,这里必须保证T和V类型是一样的,也就是参数泛型T和返回值泛型V
Function fun3 = x-> {
System.out.println("我是fun3的方法");
return x;
};
Function fun4 = y-> {
System.out.println("我是fun4的方法");
return y;
};
fun3.andThen(fun4).apply("test");
/**
* 最终结果:
* 我是fun3的方法
* 我是fun4的方法
*/
// 我们发现这里是先执行fun3的apply方法再执行fun4的apply方法的。
// compose 与andThen一样都是链式调用,但结果却大大不同,这里必须保证T和V类型是一样的,也就是参数泛型T和返回值泛型V
fun3.compose(fun4).apply("test");
/**
* 最终结果:
* 我是fun4的方法
* 我是fun3的方法
*/
// 我们发现这里是先执行的fun4的apply方法再执行fun3的apply方法的
// 由此我们推断出compose和andThen的区别就在于,compose接口方法执行顺序从右到左,而andThen则是从左到右。
Function identity = Function.identity();
// Function.identity() 静态方法这里就不好演示了,这个通常在后面搭配Stream流转Map类型的时候用到,它返回本身
}
}

学习案例

java;gutter:true; public class Main{</p> <pre><code>/** * Function函数型的函数式接口,泛型T是参数、泛型R则是返回值、主要应用场景做数据类型转换等。 * R apply(T t): 抽象方法实现,用于调用方法并返回泛型R * Function andThen: 默认实现方法,内部允许我们链式调用,与其他的andThen原理一致。 * Function compose: 默认实现方法,内部允许我们链式调用,调用方式与andThen一样,但执行顺序不一样,compose是先执行compose中的函数接口,再执行左边调用的函数接口,依次往左 * Function identity():返回当前执行的方法,从源码中我们也可以看到它返回的是当前的t */ public static void main(String[] args) { List persons = Arrays.asList(new Person(1,"张三"),new Person(2,"李四")); // 给定一个person对象集、转换成姓名属性集合返回 Function,List> fun1 = list-> { List arr = new ArrayList<>(); for (int i = 0; i < list.size(); i++) { arr.add(list.get(i).getName()); } return arr; }; List personNames = fun1.apply(persons); System.out.println(personNames); /** * 最终结果: * 结果:[张三, 李四] */ } </code></pre> <p>}</p> <p>class Person{ private Integer id; private String name;</p> <pre><code>public Person(Integer id, String name) { this.id = id; this.name = name; } public void setId(Integer id) { this.id = id; } public void setName(String name) { this.name = name; } public Integer getId() { return id; } public String getName() { return name; } </code></pre> <p>}</p> <pre><code> #### 总结 > 1.函数式接口的本质实际上就是将函数以参数的形式进行传递 2.Function是函数型的函数式接口,通常用于构建某个对象处理后返回调用环境 3.除了Function之外,还有各种函数型的函数式接口,还有BIFunction、ToIntFunction等。 #### Predicate > #### Predicate #### 基本使用 </code></pre> <p>public class Main{</p> <pre><code>/** * Predicate断言型的函数式接口,泛型T是参数、返回结果类型为布尔类型的函数接口。 * boolean test(T t): 抽象方法实现,用于返回传入的参数逻辑运算后布尔类型结果 * Predicate and: 默认实现方法,内部允许我们链式调用,用于同时判定多个Predicate函数接口的实现 类似于逻辑运算中的短路&操作。 * Predicate negate: 默认实现方法,内部允许我们链式调用,用于同时判定多个Predicate函数接口的实现,用于将当前判定结果取反后返回,类似于逻辑运算中的!操作 * Predicate or:默认实现方法,内部允许我们链式调用,用于同时判定多个Predicate函数接口的实现 类似于逻辑运算中的||操作。 * Predicate isEqual:静态方法,内部允许我们链式调用,在保证参数不是空的情况下它内部实现逻辑实际上调用的是Object的equals,具体equals看子类有没有重写 */ public static void main(String[] args) { Predicate pre1 = new Predicate() { @Override public boolean test(String o) { boolean bool = o.matches("[0-9]{1,}"); System.out.println("通过匿名内部类的方式获取到的值:"+ bool); return bool; } }; // 执行该方法的时候,它会去执行我们上述实现的test方法。 pre1.test("10086"); Predicate pre2 = text->{ boolean bool = text.matches("[0-9]{1,}"); System.out.println("通过lambda表达式的方式获取到的值:"+ bool); return bool; }; pre2.test("10086a"); /** * 最终结果: * 通过匿名内部类的方式获取到的值:true * 通过lambda表达式的方式获取到的值:false * 使用lambda表达式,我们只需要记住参数列表和执行逻辑即可,其他的我们无需关注。 */ // 我们继续对Predicate的API做一些理解和补充,毕竟这玩意在工作中经常会用上 // and 实际上等价于逻辑运算符中的短路&操作 Predicate fun3 = x-> { System.out.println("先计算fun3"); return true; }; Predicate fun4 = x-> { System.out.println("先计算fun4"); return false; }; System.out.println("第一次and结果:"+fun3.and(fun4).test("test")); /** * 最终结果: * 先计算fun3 * 先计算fun4 * 本次结果:false */ // 那么为什么我们知道它是短路&的操作 而不是&的操作呢?,我们只需要将第一个函数式接口返回false,看看它还会不会执行第二个函数式接口即可 Predicate fun5 = x-> { System.out.println("先计算fun5"); return false; }; Predicate fun6 = x-> { System.out.println("先计算fun6"); return true; }; System.out.println("第二次and结果:"+fun5.and(fun6).test("test")); /** * 最终结果: * 先计算fun5 * 第二次and结果:false。 */ // 从结果我们其实可以推断出,在第一个结果为true的情况下第二个fun6压根没进,所以是短路& // 并且起始在and方法源码中给我们也可以看到 return (t) -> test(t) && other.test(t); 是短路& // negate 实际上等价于逻辑运算符中的!操作 // 我们直接取上面的值做例子,本来结果应该为false,取反后应该为true System.out.println("negate结果:"+fun5.and(fun6).negate().test("test")); /** * 最终结果: * negate结果:true */ // or 等价于逻辑运算符中的||操作 // 我们直接取上面的做例子,第一个为false,第二个为true、||的最终结果应该为true System.out.println("or结果:"+fun5.or(fun6).test("test")); /** * 最终结果: * or结果:true */ // isEqual 内部调用的是Object的equals方法,如果子类重写了equals则调起子类的equals方法 // 如我们常用的String就重写了Object的equals方法,我们以它做例子 Predicate fun7 = Predicate.isEqual("Hello"); System.out.println("isEquals第一次结果:"+ fun7.test("Hello")); /** * 最终结果: * isEquals第一次结果:true */ Predicate fun8 = Predicate.isEqual("World"); System.out.println("isEquals第二次结果:"+ fun8.test("Hello")); // 自定义的对象类型也是可以比较的,但需要重写equals和hashCode,这里就不写示例了,可以自己玩玩 // 以上就是Predicate的相关API的介绍 } </code></pre> <p>}</p> <pre><code> #### 学习案例 ;gutter:true;
public class Main{

/**
* Predicate断言型的函数式接口,泛型T是参数、返回结果类型为布尔类型的函数接口。
* boolean test(T t): 抽象方法实现,用于返回传入的参数逻辑运算后布尔类型结果
* Predicate and: 默认实现方法,内部允许我们链式调用,用于同时判定多个Predicate函数接口的实现 类似于逻辑运算中的短路&操作。
* Predicate negate: 默认实现方法,内部允许我们链式调用,用于同时判定多个Predicate函数接口的实现,用于将当前判定结果取反后返回,类似于逻辑运算中的!操作
* Predicate or:默认实现方法,内部允许我们链式调用,用于同时判定多个Predicate函数接口的实现 类似于逻辑运算中的||操作。
* Predicate isEqual:静态方法,内部允许我们链式调用,在保证参数不是空的情况下它内部实现逻辑实际上调用的是Object的equals,具体equals看子类有没有重写
*/
public static void main(String[] args) {
// 判断给定字符串是否纯数字并且小于10 可以使用and进行链式调用
Predicate pre1 = ((Predicate) s -> s.matches("[0-9]{1,}")).and(x->Integer.valueOf(x) pre2 = s-> s.matches("[0-9]{1,}") && Integer.valueOf(s)

以上就是Jdk8提供的基础的四大函数(除了这四个之外,还有许多函数式接口,当然我们也可以自定义函数式接口)的基本使用方式和一些简单案例,具体该怎么做怎么写则需要根据项目实际需求进行,通常函数式接口都会搭配Stream成套使用,目前也有很多框架支持函数式接口的方式、如MyBatis-plus等社区活跃度较高的框架。

Original: https://www.cnblogs.com/yook2r3r/p/16705175.html
Author: 请妮妮吃火锅
Title: 【java8新特性】02:常见的函数式接口

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/615335/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • ACP 知识点总结

    记录下学习ACP过程不断遇到的且需要记录的知识点: 在阿里云专有网络VPC创建之后,路由器也是随着VPC一起自动创建,所以不需要手动创建,这个时候需要继续创建交换机才能在交换机种创…

    Java 2023年6月8日
    098
  • VMware Data Recovery备份恢复vmware虚拟机

    VMware Data Recovery 是VMware虚拟机备份工具,可创建虚拟机备份,同时不会中断虚拟机的使用或虚拟机提供的数据和服务。Data Recovery 管理现有备份…

    Java 2023年5月30日
    055
  • Spring Cloud Consul 实现服务注册和发现

    Spring Cloud 是一个基于 Spring Boot 实现的云应用开发工具,它为基于 JVM 的云应用开发中涉及的配置管理、服务发现、断路器、智能路由、微代理、控制总线、全…

    Java 2023年5月30日
    073
  • [spring]spring管理的入门项目快速搭建

    1.spring简介 Spring框架是一个开源的应用程序框架,是针对bean的生命周期进行管理的轻量级容器。 Spring解决了开发者在J2EE开发中遇到的许多常见的问题,提供了…

    Java 2023年6月6日
    080
  • MySQL之SQL语句优化

    即优化器利用自身的优化器来对我们写的SQL进行优化,然后再将其放入InnoDB引擎中执行。 移除不必要的括号 select * from x where ((a = 5)); 上面…

    Java 2023年6月16日
    050
  • 使用poi-tl导出word文件的几个技巧

    1、前言 Poi-tl提供了基于word模板文件导出word文件的功能。文档地址:http://deepoove.com/poi-tl/。 用下来,总体感觉还是很方便的。但使用过程…

    Java 2023年6月14日
    085
  • java面试——垃圾回收机制

    垃圾回收机制:——GC 初学java时。最经典的一句话是”java不像C,需要担心处理不用的内存,他有自己的垃圾回收,会自己处理的”,这是当时老师上课提过的…

    Java 2023年6月9日
    094
  • 每日一问:你了解 Java 虚拟机结构么?

    对于从事 C/C++ 程序员开发的小伙伴来说,在内存管理领域非常头疼,因为他们总是需要对每一个 new 操作去写配对的 delete/free 代码。而对于我们 Android 乃…

    Java 2023年5月29日
    080
  • TCP/IP和UDP

    TCP/IP即传输控制/网络协议,是面向连接的协议,发送数据前要先建立连接(发送方和接收方的成对的两个之间必须建 立连接),TCP提供可靠的服务,也就是说,通过TCP连接传输的数据…

    Java 2023年6月13日
    076
  • 安装typescript环境并开启VSCode自动监视编译ts文件为js文件

    一、前言 小编最近开始学习 typescript,懂得人都知道, typescript是 vue3的基础伴生,配合更加默契。就像 vue2和 js一样! typescript不像j…

    Java 2023年6月15日
    087
  • 转摘:Spring、SpringMVC和Springboot的区别

    一、概念 1、Spring Spring是一个开源容器框架,可以接管web层,业务层,dao层,持久层的组件,并且可以配置各种bean,和维护bean与bean之间的关系。其核心就…

    Java 2023年5月30日
    074
  • 延时任务-基于redis zset的完整实现

    所谓的延时任务给大家举个例子:你买了一张火车票,必须在30分钟之内付款,否则该订单被自动取消。 订单30分钟不付款自动取消,这个任务就是一个延时任务。 我之前已经写过2篇关于延时任…

    Java 2023年6月15日
    083
  • 会话技术Cookie&Session

    404. 抱歉,您访问的资源不存在。 可能是网址有误,或者对应的内容被删除,或者处于私有状态。 代码改变世界,联系邮箱 contact@cnblogs.com 园子的商业化努力-困…

    Java 2023年6月5日
    081
  • 《认知觉醒》读书笔记

    《认知觉醒》这本书分为两个部分,第一部分是 内观自己,摆脱焦虑,第二部分是 外观世界,借力前行。前者从大脑、潜意识、元认知三个方面帮助我们认识自己,审视自己内心焦虑不安的原因。后者…

    Java 2023年6月5日
    0117
  • Spring Cloud Alibaba 使用Nacos作为配置管理中心

    为什么需要配置中心? 动态配置管理是 Nacos 的三大功能之一,通过动态配置服务,我们可以在所有环境中以集中和动态的方式管理所有应用程序或服务的配置信息。 动态配置中心可以实现配…

    Java 2023年6月5日
    0113
  • spring bean依赖注入

    实例化bean对象之后,即在applyMergedBeanDefinitionPostProcessors方法中调用MergedBeanDefinitionPostProcesso…

    Java 2023年6月9日
    073
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球