11 Java 方法引用、异常处理、Java接口之函数式编程(接口知识补充Function<T,R>、BiFunction<T, U, R>和自定义泛型接口)

本文主要是介绍11 Java 方法引用、异常处理、Java接口之函数式编程(接口知识补充Function<T,R>、BiFunction<T, U, R>和自定义泛型接口),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 前言
  • 一、Java接口之函数式编程 --- 接口知识补充
    • 1 Function<T,R>泛型接口
    • 2 BiFunction<T, U, R>泛型接口
    • 3 自定义泛型函数式编程接口
    • 4 使用lambda表达式、方法引用进行函数式编程
  • 二、方法引用
    • 1 方法引用初体验(以Array.sort()方法为例)
      • (1)什么是方法引用?(怎么理解)
      • (2)为什么方法引用不用像lambda表达式一样传入参数个数?
    • 2 引用静态方法
    • 3 引用其他类成员方法


前言

一、Java接口之函数式编程 — 接口知识补充

前面在学习接口的时候很多都不知道,其中接口的有一项功能就是提供函数式编程的功能。为此Java还内置了两个专门的接口Function<T,R>和BiFunction<T, U, R>泛型接口。本节复习接口的使用和介绍这两个接口,已经到最终的自定义函数式接口(理解了这个接口就真的没有问题了)。

在前面学习stream流里面的map中间方法,里面用到了Function这个泛型接口,后来在学习方法引用,我发现这个接口结合方法引用使用有点有趣。虽然目前我很少看到有人这么干,但是我感觉很有意思,这里就来学习一下这个接口。

1 Function<T,R>泛型接口

很显然,这个函数式接口支持:

  • T 输入参数的类型。
  • R 是输出结果的类型。
    在这里插入图片描述
    源码里面这是一个函数式接口,在之前学习stream流里面的map中间方法也知道这个接口是用来定义映射关系的,简单来说就是函数关系。如果你用惯了python ,会发现java虽然说支持函数式编程,但也仅仅只是lambda表达式这种套着匿名对象类的假函数式编程。和python里面的def比较起来算个屁的函数式编程。

但是我发现Function这个接口有点 def 那个味道了,看下面例子。

/*
Function<Integer, Integer> square1 = new Function<Integer, Integer>() {@Overridepublic Integer apply(Integer x) {// 泛型中第一个参数:输入的 x 的 数据类型// 泛型中第二个参数:输出的 y 的 数据类型// x : 输入的数据// 返回值: 表示映射后的数据return x * x;}
};    // 实例化一个函数对象
int result1 = square1.apply(5);
System.out.println(result1);   // 25
*/// 使用lambda表达式的简洁定义函数对象方式
Function<Integer, Integer> square2 = x -> x * x;
int result = square2.apply(5);
System.out.println(result);   // 25

可以看到这样是不是十分接近python中的 def 了
这个接口只支持 单输入,下面的BiFunction<T, U, R>支持两个输入

2 BiFunction<T, U, R>泛型接口

很显然,这个函数式接口支持:

  • T 和 U 是输入参数的类型。
  • R 是输出结果的类型。
/*
BiFunction<Integer, Integer, Integer> add1 = new BiFunction<Integer, Integer, Integer>() {@Overridepublic Integer apply(Integer x, Integer y) {return x + y;}
};
int result1 = add1.apply(5, 3);
System.out.println(result1);  // 8
*/BiFunction<Integer, Integer, Integer> add2 = (x, y) -> x + y;
int result = add2.apply(5, 3);
System.out.println(result);  // 8

3 自定义泛型函数式编程接口

一般情况下,输入超过2个就要我们自己定义函数式接口了。

自定义接口

package cn.hjblogs.demo;@FunctionalInterface
interface TriFunction<T, U, V, R> {R apply(T t, U u, V v);
}
TriFunction<Integer,Integer,Integer,Integer> add = new TriFunction<Integer, Integer, Integer, Integer>() {@Overridepublic Integer apply(Integer x, Integer y, Integer z) {return x*y*z;}
};
int res = add.apply(2,3,5);
System.out.println(res);     // 30

4 使用lambda表达式、方法引用进行函数式编程

public class Main {public static void main(String[] args) {// 使用Lambda表达式Function<String, Integer> func1 = (String value) -> Integer.parseInt(value);// 使用方法引用Function<String, Integer> func2 = Integer::parseInt;// 两者效果相同,都是将字符串转换为整数int result1 = func1.apply("123");int result2 = func2.apply("123");System.out.println(result1); // 输出: 123System.out.println(result2); // 输出: 123}
}

这个示例充分说明了,lambda表达式、方法引用在Java中就是一个实例对象,创建了对于函数式接口的匿名内部类实例对象。

二、方法引用

  • 方法:就是以前我们学习过的一些方法。
  • 引用:就是把已经有的方法拿过来用
    怎么用?当做函数式接口中抽象方法的方法体

1 方法引用初体验(以Array.sort()方法为例)

(1)什么是方法引用?(怎么理解)

在这里插入图片描述
结合上面的图。我们来讲讲方法引用。
图中的Array.sort(数组,排序规则) 这个数组排序方法,关键在于排序规则这个参数是一个接口(准确的来说是一个函数式接口),意味着我们必须要传入这个接口的一个实现类。前面我们学过两种办法
(1)传入一个该接口的匿名内部类对象 (2)使用lambda表达式
本质上都是传进去一个该函数式接口的实现类对象。由于这是一个函数式接口,要我们重写的抽象方法只有一个,我们也可以理解为传入这个函数式接口的目的就是为了单单调用这个要求我们重写的抽象方法而已。
因此我们有了第三中传入方式
(3)方法引用:其实本质上就是lambda表达式的进一步简写形式
我们在上述的排序规则处,直接传入一个各种规则都和接口里面的要求重写的方法的形式一致的方法当做接口中抽象方法的方法体(理解成在接口的原抽象方法的方法体处调用引用的方法这样理解就可以了)

【特别注意】:引用的方法是充当接口里面方法的方法体,简单来说就在抽象方法里面调用这个方法,不是充当抽象方法,是充当抽象方法的方法体的作用

【注】这个方法可以是java或者第三方写好的,也可以是我们自己写的。必须注意,方法格式必须和函数式接口里面的方法输入参数个数、返回值、数据类型等等这些严格对应(只有这样底层才能自动推导出来)。

  • 被引用的方法必须已经存在
  • 被引用方法的形参和返回值需要跟抽象方法保持一致
  • 被引用方法的功能要满足当前需求

说白了,就是以前传匿名内部类、lambda表达式、现在方法引用就是传一个方法放进接口里面方法的方法体;其实底层还是在传接口的实现类。

下面演示一下就清楚了:

public class Test {public static void main(String[] args) {Integer[] arr = {3, 5, 4, 1, 6, 2};/*// 匿名内部类Arrays.sort(arr, new Comparator<Integer>() {@Overridepublic int compare(Integer o1, Integer o2) {return o1 - o2;}});*//*// lambda表达式Arrays.sort(arr, (o1, o2) -> o1 - o2);*/// 方法引用// 把这个方法当做函数式接口抽象方法的方法体Arrays.sort(arr, Test::my_compare);   // 方法引用   类名::方法名System.out.println(Arrays.toString(arr)); // [1, 2, 3, 4, 5, 6]}// 自定义比较方法,要被方法引用的public static int my_compare(int num1, int num2){return num1 - num2;}}
  • :: 符号是方法引用符

上面的方法引用其实可以等价成下面这样理解就好了(帮助理解)---- 我觉得这样理解是最好的

// ============= 下面是上面方法引用的等价
// 等价于下面 lambda表达式
// Arrays.sort(arr, (o1, o2) -> Test.my_compare(o1, o2));// 等价于下面 匿名内部类
Arrays.sort(arr, new Comparator<Integer>() {@Overridepublic int compare(Integer o1, Integer o2) {return Test.my_compare(o1, o2);  // 等价于 Test::my_compare,在方法体中调用引用方法}
});

关于为什么方法引用不许要像lambda表达式一样写出输入参数个数。

(2)为什么方法引用不用像lambda表达式一样传入参数个数?

当你使用方法引用时,虽然你没有显式地写出参数,但实际上参数是隐式传递的。Java 编译器会根据上下文自动推导出参数,并将它们传递给引用的方法。

具体说明:以下面的 Integer::parseInt 为例,它实际上等价于Lambda表达式 (String value) -> Integer.parseInt(value),这里的 value 是Lambda表达式中的参数。在使用方法引用时,编译器会自动处理参数的传递。

public class Main {public static void main(String[] args) {// 使用Lambda表达式Function<String, Integer> func1 = (String value) -> Integer.parseInt(value);// 使用方法引用Function<String, Integer> func2 = Integer::parseInt;// 两者效果相同,都是将字符串转换为整数int result1 = func1.apply("123");int result2 = func2.apply("123");System.out.println(result1); // 输出: 123System.out.println(result2); // 输出: 123}
}

2 引用静态方法

  • 格式: 类名::静态方法
  • 范例: Integer : :parseInt

3 引用其他类成员方法

这篇关于11 Java 方法引用、异常处理、Java接口之函数式编程(接口知识补充Function<T,R>、BiFunction<T, U, R>和自定义泛型接口)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1130955

相关文章

Java中流式并行操作parallelStream的原理和使用方法

《Java中流式并行操作parallelStream的原理和使用方法》本文详细介绍了Java中的并行流(parallelStream)的原理、正确使用方法以及在实际业务中的应用案例,并指出在使用并行流... 目录Java中流式并行操作parallelStream0. 问题的产生1. 什么是parallelS

MySQL数据库双机热备的配置方法详解

《MySQL数据库双机热备的配置方法详解》在企业级应用中,数据库的高可用性和数据的安全性是至关重要的,MySQL作为最流行的开源关系型数据库管理系统之一,提供了多种方式来实现高可用性,其中双机热备(M... 目录1. 环境准备1.1 安装mysql1.2 配置MySQL1.2.1 主服务器配置1.2.2 从

Java中Redisson 的原理深度解析

《Java中Redisson的原理深度解析》Redisson是一个高性能的Redis客户端,它通过将Redis数据结构映射为Java对象和分布式对象,实现了在Java应用中方便地使用Redis,本文... 目录前言一、核心设计理念二、核心架构与通信层1. 基于 Netty 的异步非阻塞通信2. 编解码器三、

C++中悬垂引用(Dangling Reference) 的实现

《C++中悬垂引用(DanglingReference)的实现》C++中的悬垂引用指引用绑定的对象被销毁后引用仍存在的情况,会导致访问无效内存,下面就来详细的介绍一下产生的原因以及如何避免,感兴趣... 目录悬垂引用的产生原因1. 引用绑定到局部变量,变量超出作用域后销毁2. 引用绑定到动态分配的对象,对象

SpringBoot基于注解实现数据库字段回填的完整方案

《SpringBoot基于注解实现数据库字段回填的完整方案》这篇文章主要为大家详细介绍了SpringBoot如何基于注解实现数据库字段回填的相关方法,文中的示例代码讲解详细,感兴趣的小伙伴可以了解... 目录数据库表pom.XMLRelationFieldRelationFieldMapping基础的一些代

一篇文章彻底搞懂macOS如何决定java环境

《一篇文章彻底搞懂macOS如何决定java环境》MacOS作为一个功能强大的操作系统,为开发者提供了丰富的开发工具和框架,下面:本文主要介绍macOS如何决定java环境的相关资料,文中通过代码... 目录方法一:使用 which命令方法二:使用 Java_home工具(Apple 官方推荐)那问题来了,

Java HashMap的底层实现原理深度解析

《JavaHashMap的底层实现原理深度解析》HashMap基于数组+链表+红黑树结构,通过哈希算法和扩容机制优化性能,负载因子与树化阈值平衡效率,是Java开发必备的高效数据结构,本文给大家介绍... 目录一、概述:HashMap的宏观结构二、核心数据结构解析1. 数组(桶数组)2. 链表节点(Node

Java AOP面向切面编程的概念和实现方式

《JavaAOP面向切面编程的概念和实现方式》AOP是面向切面编程,通过动态代理将横切关注点(如日志、事务)与核心业务逻辑分离,提升代码复用性和可维护性,本文给大家介绍JavaAOP面向切面编程的概... 目录一、AOP 是什么?二、AOP 的核心概念与实现方式核心概念实现方式三、Spring AOP 的关

详解SpringBoot+Ehcache使用示例

《详解SpringBoot+Ehcache使用示例》本文介绍了SpringBoot中配置Ehcache、自定义get/set方式,并实际使用缓存的过程,文中通过示例代码介绍的非常详细,对大家的学习或者... 目录摘要概念内存与磁盘持久化存储:配置灵活性:编码示例引入依赖:配置ehcache.XML文件:配置

Java 虚拟线程的创建与使用深度解析

《Java虚拟线程的创建与使用深度解析》虚拟线程是Java19中以预览特性形式引入,Java21起正式发布的轻量级线程,本文给大家介绍Java虚拟线程的创建与使用,感兴趣的朋友一起看看吧... 目录一、虚拟线程简介1.1 什么是虚拟线程?1.2 为什么需要虚拟线程?二、虚拟线程与平台线程对比代码对比示例:三