【从零开始学习JAVA | 第三十篇】方法引用

news2025/1/8 5:10:44

前言:

方法引用作为一个重要的知识点,虽然他使用起来很复杂,而且会降低代码的可读性,但是如果用好了方法引用,我们也会获得不错的效率,因此我们在今天将为大家讲解什么是方法引用。

方法引用:

方法引用基本概念:

我们用一张图就很好的解释了什么叫做方法引用,而官方对于方法引用的定义为:

Java中,方法引用是一种简洁的写法,用于直接引用现有方法,而不是调用它们。它提供了一种更简洁、易读的方式来传递方法作为参数或在函数式接口中使用。

总而言之:方法引用就是我们把已经有的方法拿过来,当作函数式接口中的抽象方法的方法体。

我们举一个例子:

假设要对一个式子进行排序,我们用lambda表达式的写法,可以写为:

Arrays.sort(arr,new Comparator<Integer>(){
    @override
    public int compare (Integer o1 , Integer o2)
    {
    return o2-o1;
    }

});

而如果此时我们的代码中就已经有一个排序的方法 subraction:

public int subraction (int n1.int n2)
{
    return n2-n1;
}

那么我们就可以直接在排序函数中直接拿过来用:

Arrays.sort(arr,方法所在类名::subtraction);

//用我们已经实现的方法subtraction作为排序的规则

其实这就是方法的引用。但是 不是所有的方法都可以引用的!

方法可以被引用的条件:

在Java中,方法引用要满足以下条件才能被引用:

1. 方法引用必须引用一个已存在的方法。也就是说,被引用的方法必须已经被定义。

2. 方法引用的参数类型和数量必须与函数式接口中的抽象方法的参数类型和数量相匹配。也就是说,被引用的方法的参数列表要与函数式接口中的方法参数列表完全匹配。

3. 方法引用的返回类型必须与函数式接口中的抽象方法的返回类型相匹配。也就是说,被引用的方法的返回类型要与函数式接口中的方法返回类型完全匹配。

4. 方法引用必须在上下文中根据目标类型进行推断。也就是说,编译器必须能够根据方法引用的上下文确定需要引用哪个方法。

需要注意的是,方法引用可以用于函数式接口中的抽象方法,包括Lambda表达式和方法引用在内。函数式接口是只有一个抽象方法的接口,可以使用@FunctionalInterface注解来标记。

方法引用的种类:

1.引用静态方法。

        格式:类名::静态方法

        例子:Integer::parseInt

    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list, "1", "2", "3", "4", "5");

        //普通方法
        list.stream().map(new Function<String, Integer>() {
            @Override
            public Integer apply(String s) {
                int i = Integer.parseInt(s);
                return i;

            }
        }).forEach(s-> System.out.println(s));

        //方法引用
        list.stream().map(Integer::parseInt).forEach(s-> System.out.println(s));
    }
}

2.引用成员方法。

        格式:对象::成员方法

        1️⃣其他类:   其他类对象:: 方法名

案例:从一组字符串姓名中选出姓张且名字是三个字的人:

创建其他类:

public class Stringoperation {
    public boolean stringjudge(String s) {
        return s.startsWith("张")&& s.length()==3;
    }
}

引用其他类中的方法:

    public static void main(String[] args) {

        //1.创建集合并添加数据
        ArrayList<String>list= new ArrayList<>();
        Collections.addAll(list,"张无忌","周芷若","张敏","张强","张三丰");

        //普通写法
        list.stream().filter(s->s.startsWith("张")).filter(s->s.length()==3).forEach(s-> System.out.println(s));

        //匿名内部类写法
        list.stream().filter(new Predicate<String>() {
            @Override
            public boolean test(String s) {
                return s.startsWith("张")&& s.length()==3;
            }
        }).forEach(s-> System.out.println(s));

        //方法引用
        list.stream().filter(new Stringoperation()::stringjudge).forEach(s-> System.out.println(s));


    }

        2️⃣本类:          this::方法名  (引用处不能是静态方法)

import java.util.function.Consumer;
public class test07 {



        public void doSomething() {
            Consumer<String> consumer = this::printMessage; // 使用this::printMessage引用本类方法
            consumer.accept("Hello, world!");
        }

        public void printMessage(String message) {
            System.out.println(message);
        }

        public static void main(String[] args) {
            test07 obj = new test07();
            obj.doSomething();
        }


}

 TIPS:

        this::方法名  这种形式是不可以在静态main方法中实现的,这是因为main有前缀static,使得main方法变为了静态方法,而静态方法中是没有this指针的,如果想要在静态方法中使用本类方法,就要重新创建一个本类的对象,然后再引用。

public class test06 {
    public static void  main(String[] args) {

        //1.创建集合并添加数据
        ArrayList<String>list= new ArrayList<>();
        Collections.addAll(list,"张无忌","周芷若","张敏","张强","张三丰");

        //方法引用
        list.stream().filter(new test06()::stringjudge).forEach(s-> System.out.println(s));


    }
    public boolean stringjudge(String s) {
        return s.startsWith("张")&& s.length()==3;
    }
}

        3️⃣父类:        super::方法名 (引用处不能是静态方法)                        

class Parent {
    public void printMessage() {
        System.out.println("Hello from Parent class");
    }
}

class Child extends Parent {
    public void printMessage() {
        // 使用super::printMessage引用父类的方法
        Runnable runnable = super::printMessage;
        runnable.run();
    }

    public static void main(String[] args) {
        Child child = new Child();
        child.printMessage();
    }
}

3.引用构造方法。

        格式:类名:: new

        范例:Student :: new

class Person {
    private String name;

    public Person(String name) {
        this.name = name;
    }

    public void introduce() {
        System.out.println("Hello, my name is " + name);
    }
}

interface PersonFactory {
    Person create(String name);
}

class Example {
    public static void main(String[] args) {
        // 使用构造方法引用创建Person对象
        PersonFactory factory = Person::new;
        Person person = factory.create("Alice");

        person.introduce();
    }
}

4.使用类名引用成员方法

        格式:类名::成员方法

        范例:String::substring

public class test08 {
    public static void main(String[] args) {

        //1.创建集合对象并且添加对象
        ArrayList<String> list = new ArrayList<>();

        Collections.addAll(list, "aaa", "bbb", "ccc");

        //2.变成大写之后再输出
        //匿名内部类写法
        list.stream().map(new Function<String, String>() {
            @Override
            public String apply(String s) {
                return s.toUpperCase();
            }
        }).forEach(s -> System.out.println(s));

        //引用类中的方法
        list.stream().map(String::toUpperCase).forEach(s -> System.out.println(s));
    }

}

而使用类名引用成员方法在某些使用规则上与我们最开始的定义不同:

1.被引用方法的形参必须和抽象方法的第二个形参到最后一个形参保持一致,返回值需要保持一致。

抽象方法形参详解:

第一个参数:表示被引用方法的调用者,决定了可以引用哪些类中的方法                                                              在stream流当中,第一个参数一般都表示流里面的每一个数据。                                                      假设现在流里面的数据是字符串,那么使用这种方式进行方法引用的时候                                        只能使用String这给类中的方法。

第二个参数到最后一个参数:跟被引用方法的形参保持一致,如果没有第二个参数                                                                         那么说明被引用的方法需要是无参的成员方法 

总而言之:使用类名引用成员方法的时候,并不是所有的类名都可以使用,只可以使用抽象方法中第一个参数对应的类型。

而这也就是这种操作的局限性,它限制了我们可以使用方法的种类!

5.引用数组的构造方法:

        格式:数据类型::new 

        范例:int [] new

        细节:数组的类型需要和流中的数组类型保持一致。

public class test09 {
    public static void main(String[] args) {
        //1.创建集合并添加元素
        ArrayList<Integer> list = new ArrayList<>();

        Collections.addAll(list,1,2,3,4,5,6,7,8,9);

        //2.收集到数组中
            //匿名内部类
       Integer[] arr= list.stream().toArray(new IntFunction<Integer[]>() {
            @Override
            public Integer[] apply(int value) {
                return new Integer[value];
            }
        });

        for (Integer integer : arr) {
            System.out.println(integer);
        }
            //方法引用
        Integer[] array = list.stream().toArray(Integer[]::new);
        for (Integer integer : array) {
         System.out.println(integer);
       }

    }
}

方法引用的优点:

方法引用(Method Reference)是Java中函数式编程的一项重要特性,它允许我们通过方法的名称来引用一个已经存在的方法,可以看作是Lambda表达式的简化形式。方法引用的优点包括:

1. 简洁性:方法引用能够将繁琐的Lambda表达式进一步简化,使代码更加简洁、直观。通过引用现有的方法,避免了再次编写大量的重复代码。

2. 可读性:方法引用能够提高代码的可读性和可理解性。通过使用已有方法的名称,可以更直观地表示代码的意图和逻辑。

3. 代码复用:方法引用使得代码重用更加方便。可以将已存在的、已经实现的方法作为引用直接传递,从而减少了重复的方法定义和实现。

4. 维护性和一致性:使用方法引用可以使代码更具有一致性和易于维护。当需要修改方法逻辑时,只需要修改被引用的方法,而无需修改引用该方法的所有地方。

5. 高效性:方法引用本质上是通过对已经存在的方法进行复用来实现的,因此在执行效率上可能会更高一些。

需要注意的是,方法引用并不是适用于所有场景,它有一些使用限制,比如需要满足函数式接口的要求,被引用的方法与函数式接口的抽象方法有着相同的参数列表和返回类型等。

总结:

        方法引用的出现,进一步优化了lambda表达式的代码逻辑,优化了我们代码的效率,是一个不错的武器,用来提高我们代码的复用性,一致性和高效性,我们要掌握好方法引用这一好武器。

如果我的内容对你有帮助,请点赞,评论,收藏。创作不易,大家的支持就是我坚持下去的动力!

 

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

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

相关文章

J-Flash合并多个bin文件

文章目录 1. 前言2. 下载 J-Flash 工具3. 合并3个bin文件3.1 Booloader.bin3.2 APP1.bin3.3 APP2.bin3.4 保存 → 导出合并bin文件3.5 未用到的区域会被填充为 FF 4. 欢迎纠正~ 1. 前言 下面介绍用J-Flash工具合并多个bin文件的方法 2. 下载 J-Flash 工具 在下载Jink驱动的…

excel 生成sql技巧

"update 表名 set 字段名"&A2&" where 字段名"&B2&";"

最小栈,设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。

题记&#xff1a; 设计一个支持 push &#xff0c;pop &#xff0c;top 操作&#xff0c;并能在常数时间内检索到最小元素的栈。 实现 MinStack 类: MinStack() 初始化堆栈对象。void push(int val) 将元素val推入堆栈。void pop() 删除堆栈顶部的元素。int top() 获取堆栈顶…

交换机和终端设备的基本配置

1 IOS访问 1.1 操作系统 所有终端设备和网络设备都需要有操作系统 (OS)。如图所示&#xff0c;操作系统中直接与计算机硬件交互的部分称为内核。与应用程序和用户连接的部分则称为外壳。用户可以使用命令行界面 (CLI) 或图形用户界面 (GUI) 与外壳交互。 使用 CLI 时&#xf…

欧姆龙以太网模块连接MCGS步骤

你是否曾经遇到过这样的问题&#xff1a;在监控PLC数据时&#xff0c;触摸屏无法与PLC通讯&#xff0c;或者PLC的通讯口被占用了&#xff1f;今天&#xff0c;我要向大家介绍一款神奇的设备——捷米特JM-ETH-CP转以太网模块&#xff0c;它能够即插即用&#xff0c;不占用PLC通讯…

被吹爆的Wi-Fi 6,究竟强在哪?

被吹爆的Wi-Fi 6&#xff0c;究竟强在哪&#xff1f; 伴随IoT技术的成熟及发展&#xff0c;Wi-Fi标准也在不断迭代升级&#xff0c;然而就实际情况来说&#xff0c;依旧有不少人仍在使用旧标准&#xff0c;比如Wi-Fi 4、Wi-Fi 5。其实WiFi的每一次升级除了速率更高&#xff0c;…

SpringCloud+Nacos集成Seata-1.7.0分布式事务

Seata是一个比较成熟的分布式事务工具&#xff0c;非常好用&#xff0c;主流的的一套&#xff0c;网上大多都是1.4版本&#xff0c;以及不完整了&#xff0c;鄙人也是找了好久才找到有个1.7版本的详细教程&#xff08;放在最后面了&#xff0c;毕竟是别人的技术&#xff0c;这里…

打造高效便捷的采购管理平台,提升企业采购效率

随着企业规模的扩大和供应链的日益复杂&#xff0c;传统的手工采购管理方式已经不能满足企业的需求。采购管理平台的出现为企业提供了一个集中、高效、便捷的采购管理工具。本文将重点探讨采购管理平台的意义与作用&#xff0c;并介绍如何打造一个高效便捷的采购管理平台。 一、…

【干货分享】如何恢复SOLIDWORKS 零件、装配体和工程图模板?

当我们卸载了SOLIDWORKS或者是购买了一台新笔记本电脑或是丢失了一直在使用的模板时&#xff0c;我们可以通过打开过去的零件、装配体和工程图文件来恢复 SOLIDWORKS 模板。 ▷ 零件模板 打开包含所需自定义属性的上一个部件。 保存零件的副本以避免对原始文件进行意外更改。…

机器学习深度学习——线性回归的从零开始实现

虽然现在的深度学习框架几乎可以自动化实现下面的工作&#xff0c;但从零开始实现可以更了解工作原理&#xff0c;方便我们自定义模型、自定义层或自定义损失函数。 import random import torch from d2l import torch as d2l线性回归的从零开始实现 生成数据集读取数据集初始…

【技术】国标GB28181视频监控平台EasyGBS视无法播放,抓包返回ICMP

视频流媒体安防监控国标GB28181平台EasyGBS视频能力丰富&#xff0c;部署灵活&#xff0c;既能作为业务平台使用&#xff0c;也能作为安防监控视频能力层被业务管理平台调用。国标GB28181视频EasyGBS平台可提供流媒体接入、处理、转发等服务&#xff0c;支持内网、公网的安防视…

Ansys Speos | Presets 适合用户的预定义参数集

概述 Speos Presets 参数预置功能允许创建预定义的参数集&#xff0c;并将它们应用于新的或现有的 Speos&#xff0c;从任何 Speos 对象创建预设&#xff0c;例如光源&#xff0c;传感器&#xff0c;材料&#xff0c;仿真等&#xff0c;通过一个*.Preset 的文件定对仿真类型的配…

C++之文件操作

1.C文件操作 C中文件操作头文件:fstream。   文件类型&#xff1a;文件文件和二进制文件。 文件操作三大类&#xff1a;     ofstream 写操作     ifstream 读操作     fstream:读写操作 文件打开方式&#xff1a; 标志说明ios::in只读ios::out只写,文件不存在则…

Spring系列一:spring的安装与使用

文章目录 &#x1f49e; 官方资料&#x1f34a;Spring5下载&#x1f34a;文档介绍 &#x1f49e;Spring5&#x1f34a;内容介绍&#x1f34a;重要概念 &#x1f49e;快速入门&#x1f34a;Spring操作演示&#x1f34a;类加载路径&#x1f34a;Debug配置&#x1f34a;Spring容器…

基于Centos 7虚拟机的磁盘操作(添加磁盘、分区、格式分区、挂载)

目录 一、添加硬盘 二、查看新磁盘 三、磁盘分区 3.1新建分区 3.2 格式分区 3.3 挂载分区 3.4 永久挂载新分区 3.5 取消挂载分区 一、添加硬盘 1.在虚拟机处选择编辑虚拟机设置&#xff0c;然后选择添加 2.选择硬盘&#xff0c;然后选择下一步 3.默认即可&#xff0c;下一步…

啤酒节,燃起青岛啤酒们的“热血”

【潮汐商业评论/ 原创】 “这周五晚上我们就出发&#xff01;三年了&#xff0c;终于可以再去啤酒节畅快淋漓了&#xff01;”作为啤酒爱好者Joe兴奋道。 随着线下经济的复苏&#xff0c;疫情后的第一个盛夏正在被全国各地的“啤酒狂欢”所点燃。 7月14日晚&#xff0c;随着…

Canal安装部署与测试

文章目录 第一章 Canal概述1.1 简介1.2 工作原理1.2.1 MySQL主备复制原理1.2.2 canal 工作原理 1.3 重要版本更新说明1.4 多语言 第二章 Canal安装部署2.1 准备2.2 canal安装 第三章 Canal和Kafka整合测试注意事项 第一章 Canal概述 Github地址&#xff1a;https://github.com…

脑电信号处理与特征提取——4.脑电信号的预处理及数据分析要点(彭微微)

目录 四、脑电信号的预处理及数据分析要点 4.1 脑电基础知识回顾 4.2 伪迹 4.3 EEG预处理 4.3.1 滤波 4.3.2 重参考 4.3.3 分段和基线校正 4.3.4 坏段剔除 4.3.5 坏导剔除/插值 4.3.6 独立成分分析ICA 4.4 事件相关电位&#xff08;ERPs&#xff09; 4.4.1 如何获…

Java子类可以继承父类的所有属性吗

子类可以继承父类的所有属性吗 答案&#xff1a; 不可以&#xff0c;当然是不可以全部属性和方法都继承&#xff0c;那么哪些不可以继承&#xff1f; 最起码私有的就不可以&#xff0c;私有的属性和方法都不可以&#xff0c;其他的那就需要继续去测试了。 我用的是jdk1.8来…

Docker 的数据管理、镜像的创建

Docker 的数据管理、镜像的创建 Docker 的数据管理1&#xff0e;数据卷2&#xff0e;数据卷容器端口映射容器互联&#xff08;使用centos镜像&#xff09; Docker 镜像的创建1&#xff0e;基于现有镜像创建&#xff08;1&#xff09;首先启动一个镜像&#xff0c;在容器里做修改…