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

news2024/11/15 9:33:07

文章目录

  • 前言
  • 一、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>() {
    @Override
    public 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>() {
    @Override
    public 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>() {
    @Override
    public 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); // 输出: 123
        System.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>() {
            @Override
            public 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>() {
    @Override
    public 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); // 输出: 123
        System.out.println(result2); // 输出: 123
    }
}

2 引用静态方法

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

3 引用其他类成员方法

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2098136.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

当了中层才发现:领导根本不在意,你干了多少活

在职场的棋盘上&#xff0c;每个人都是一枚棋子&#xff0c;而中层领导则像是那些走在前线的骑士——既要冲锋陷阵&#xff0c;又要运筹帷幄。但你有没有想过&#xff0c;领导真的在意你加班到深夜&#xff0c;或是周末还在回复邮件吗&#xff1f; 当你从基层一步步爬到中层&a…

Unity(2022.3.41LTS) - UI详细介绍-Slider(滑动条)

目录 零.简介 一、基本功能与用途 二、组件介绍 零.简介 在 Unity 中&#xff0c;Slider&#xff08;滑动条&#xff09;是一个可以滑动的 UI 组件. 一、基本功能与用途 数值调节&#xff1a;主要功能是让用户在一个特定的数值范围内进行选择。例如&#xff0c;可以用于调…

深度学习之张量(Tensor)的创建、常见属性及数据转换

基本概念 PyTorch会将数据封装成张量&#xff08;Tensor&#xff09;进行计算&#xff0c;所谓张量就是元素为相同类型的多维矩阵。 张量是一个多维数组&#xff0c;通俗来说可以看作是扩展了标量、向量、矩阵的更高维度的数组。张量的维度决定了它的形状&#xff08;Shape&a…

同城搭子系统小程序开发产品分析

1. 市场调研与需求分析 目标用户定位&#xff1a;定义核心用户群&#xff0c;例如上班族、学生、旅游爱好者等。需求收集&#xff1a;运用问卷调查、用户访谈等方法收集用户对功能的具体需求&#xff0c;特别是对安全、便捷性和费用分摊的关注点。竞品分析&#xff1a;分析同类…

开心第一课:健康坐姿

文章目录 引言保持脊柱的自然伸展选择一把合适的座椅引言 建议坐位时间超过 30 分钟,就起身活动一下,促进血液循环,预防久坐带来的各种健康问题。 保持脊柱的自然伸展 正确的骨盆位置是使坐位时身体重量都作用在双侧的坐骨结节上,在结节的顶端有滑囊,滑囊分泌液体减少组…

由“为什么VO不能继承PO?” 引出的为什么组合优于继承?

简述VO、DTO、PO的概念。 如下概念是我个人的理解&#xff1a; VO&#xff08;View Object&#xff09;&#xff1a; 视图对象&#xff0c;用于展示&#xff0c;这很好理解&#xff0c;就是前端页面所需数据封装&#xff0c;一般所需要的属性比 PO 多并且。DTO&#xff08;Da…

二手电脑配置给你不一样的成就感之四

今天测了e3 1220 v1 没有想象中的好&#xff0c;鲁大师才评20多万分&#xff0c;比fm2平台6600k强一点点有限。 6600k fm2平台是20万出头。cpuz的评分还是比较高&#xff0c;实际使用效果比6600k稍好一些。 单盒能力确实比apu强一些。同样拿world frvr 测试&#xff0c;调到最低…

微分方程_by小崔说数

可降解的微分方程 不显含x&#xff1a;y两撇dp/dxdp/dy*dy/dx 不显含y&#xff1a;dp/dx 都是y撇等于p 自变量与因变量呼唤 讲解为一阶线性微风方程 &#xff0c;变成可分离得 公式得 高阶可降解得微分方程 通解非齐次特解齐次通解 非齐次特解&#xff1b;解得叠加原…

旗晟智能助推浙大海创人形机器人创新中心发展!

8月27日上午&#xff0c;余杭区成功举办机器人产业高质量发展大会。会上&#xff0c;由地方政府与浙江大学共同建设的人形机器人产业创新中心成立并揭牌。该中心目标明确&#xff0c;致力于打造机器人领域国内、国际的高等级创新高地。浙江大学校领导、院士专家、机器人企业代表…

js调试--本地替换

js调试--本地替换 一、本地替换的作用二、操作方法(以百度首页为例)1、选中目标资源地址二、替换为本地资源三、修改本地内容一、本地替换的作用 本地替换的作用就是将原本访问服务器的数据改成访问本地。 二、操作方法(以百度首页为例) 1、选中目标资源地址 以百度首页…

智能工厂MES实施规划

智能工厂MES&#xff08;制造执行系统&#xff09;实施规划是一个复杂而系统的过程&#xff0c;旨在通过数字化手段提升工厂的生产效率、降低成本并提高产品质量。以下是一个全面的智能工厂MES实施规划方案&#xff0c;涵盖主要步骤和关键点&#xff1a; 一、前期准备与需求分析…

手撸瀑布流

一、需求&#xff1f; 要求实现 一排两列 瀑布流样式&#xff0c;样式如下&#xff1a;其中A为容器&#xff0c;B为组件样式&#xff0c;卡片高度会因为标题的多少来自适应。 二、解法 1.使用CSS的column&#xff08;⚠️不推荐&#xff09; 使用CSS 属性 column 用来设置…

朋友们!pinterest视频保存教学给你们做出来啦

有没有在为pinterest视频无法下载而烦恼的小伙伴呀&#xff1f;我反正是被这个困扰好久了&#xff0c;每每看到喜欢的视频素材不能下载&#xff0c;真的很捉急&#xff01;最近发现了这款神器&#xff0c;就是之前给大家推荐【水印抹布】app可以完美解决pinterest视频保存的问题…

Vue2与Vue3区别以及兼容那些浏览器酌情使用

一、vue2与vue3区别 vuejs/vue: 是一个用于构建用户界面的 JavaScript 框架,具有简洁的语法和丰富的组件库,可以用于开发单页面应用程序和多页面应用程序。 项目地址:https://gitcode.com/gh_mirrors/vu/vue 免费下载资源 1.根节点不同 vue2中必须要有根标签vue3中可以没…

pod进阶:

pod进阶&#xff1a; pod的生命周期当中的状态&#xff1a; 1、Running 运行中&#xff0c;pod已经分配到节点上&#xff0c;且pod内的容器正常运行。正常状态&#xff08;ready 1/1&#xff09; 2、complete 完成之后退出&#xff0c;容器内的返回码是0 echo $? 表示容器是…

基于Easy-Wav2lip-Gradio的AI数字人

数字人技术近年来在多个领域得到广泛应用,从虚拟主播到电影特效,都需要精确的音视频同步技术来实现逼真的效果。传统的嘴型同步技术往往面临着准确性不高、处理速度慢等问题。为了解决这些问题,原有的Wav2Lip项目应运而生。Wav2Lip通过人工智能技术,实现了音频和视频的高精…

从LED硬件控制流程认识Android架构!

0.Android架构图 一上来就是一张框架图&#xff0c;发明了框架图的天才真是个天才&#xff01; 简单点评一下&#xff1a; 1.对于安卓应用开发来说&#xff1a;App层以下都是操作系统&#xff0c;反正我只关心Android Studio&#xff08;其实是SDK&#xff09;给我什么包&…

字节-人工智能编辑代码方向面试-题目记录

问的都是自己简历里写的&#xff01;不会就不要写 简述一下几个排序算法 二分查找的时间复杂度&#xff08;是O&#xff08;log2n&#xff09;!!!&#xff09; find函数和count函数的时间复杂度都是O&#xff08;n&#xff09;&#xff0c;因为都是遍历整个数组来找的&#x…

Ubuntu Linux Server安装Kubernetes

本文主要描述在Ubuntu Linux Server操作系统中安装Kubernetes云原生对应的microk8s组件。 sudo snap install microk8s --classic 如上所示&#xff0c;在Ubuntu服务器中安装microk8s组件完成&#xff0c;对应的版本是microk8s v1.30版本 microk8s enable dashboard 如上所…

Java小白一文讲清Java中集合相关的知识点(二)

List List接口和常用方法 基本介绍 List接口是Collection接口的子接口 List集合类中的元素有序–即添加顺序和取出顺序一致、且可重复 public class Journey {SuppressWarnings({"all"})public static void main(String[] args) {List list new ArrayList();li…