JAVA方法引用:

news2024/11/15 13:55:42
  • 方法引用的出现原因在使用Lambda表达式的时候,我们实际上传递进去的代码就是一种解决方案:拿参数做操作那么考虑一种情况:如果我们在Lambda中所指定的操作方案,已经有地方存在相同方案,那是否还有必要再写重复逻辑呢?答案肯定是没有必要那我们又是如何使用已经存在的方案的呢?这就是我们要讲解的方法引用,我们是通过方法引用来使用已经存在的方案

概述:

方法引用就是把已经有的方法拿过来用,当做函数式接口中的抽象方法的方法体
以后会在MybatisPlus这个框架中大量使用方法引用


如下过程就是方法引用:
要求:对Integer数组arr进行降序排序:
image.png
这时可以用方法引用 ,当做函数式接口中的抽象方法的方法体
image.png

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


Integer []arr={4,2,5,7,8};
//1.普通表达式
/*  Arrays.sort(arr, new Comparator<Integer>() {
    @Override
    public int compare(Integer o1, Integer o2) {
        return o2-o1;
    }
});*/

        
//2.lambda表达式
/*Arrays.sort(arr, ((o1, o2) -> o2-o1));*/


//3.方法引用:
//表示引用Test01类里面的subtraction
//把这个方法当做抽象方法的方法体
Arrays.sort(arr,Test01::subtraction);
//打印
System.out.println(Arrays.toString(arr));//[8, 7, 5, 4, 2]
    }

    //可以是Java己经写好的,也可以是一些第三方的工具类
    public static int subtraction(Integer num1,Integer num2){
        return num2-num1;
    }
}

::是方法引用符


## 使用方法引用必须满足下面四点:
  1. 引用处必须是函数式接口
    1. 如上:Compartor是函数式接口
  2. 被引用的方法必须已经存在
    1. 如上:subtraction已手动创建
  3. 被引用的方法的形参和返回值要和抽象方法保持一致
  4. 被引用方法的功能必须满足需求

方法引用的分类:

引用静态方法:

格式:类名::静态方法名
如:Integer::parseInt

我们上面那个的例子其实也是引用静态方法

练习:
image.png

public class Test02 {
    public static void main(String[] args) {
//引用静态方法
ArrayList<String>list=new ArrayList<>();
Collections.addAll(list,"1","2","3","4","5");
//类型转换可以用刚学的stream流中的中间方法map

//1.普通表达式
/*List<Integer> newList = list.stream()
        .map(new Function<String, Integer>() {
    @Override
    public Integer apply(String s) {
        return Integer.parseInt(s);
    }
}).collect(Collectors.toList());*/

        
//2.方法引用
List<Integer> newList = list.stream()
        .map(Integer::parseInt)
        .collect(Collectors.toList());
//获取0索引看看可以运算吗
int num = newList.get(0) + 1;
System.out.println(num);//2,可以运算说明成功转为Integer类型了

    }
}

可以看看上面的方法引用是否满足四个要求:

  1. Function是函数式接口
    1. image.png
  2. 被引用的方法是java已存在的
  3. 被引用的parseInt方法的形参和返回值和抽象方法一致
    1. Snipaste_2024-02-03_10-50-16.png
  4. 被引用的方法的功能必须满足需求

引用成员方法

引用其他类的成员方法:

格式: 对象::成员方法

练习:
image.png

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


ArrayList<String>list=new ArrayList<>();
Collections.addAll(list,"张三","钱七","王五","张无忌","李四");
        
//要求:过滤数据(只要以张开头,而且名字是3个字的)

/*//普通写法
list.stream()
        .filter(new Predicate<String>() {
            @Override
            public boolean test(String s) {
                return s.startsWith("张")&&s.length()==3;
            }
        });*/

//方法引用
//创建对象:
StringOperation so=new StringOperation();
list.stream()
        .filter(so::method)
        .forEach(s -> System.out.println(s));//张无忌
    }
}
//其他类:
class StringOperation{
    //成员方法
    public boolean method(String s) {
        return s.startsWith("张")&&s.length()==3;
    }
}

引用本类的成员方法(引用处不能是静态)

格式:this::方法名

还是用上面这个例子:
此时将其他类的成员方法放到本类中,

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


ArrayList<String>list=new ArrayList<>();
Collections.addAll(list,"张三","钱七","王五","张无忌","李四");
//过滤数据(只要以张开头,而且名字是3个字的)

/*//普通写法
list.stream()
        .filter(new Predicate<String>() {
            @Override
            public boolean test(String s) {
                return s.startsWith("张")&&s.length()==3;
            }
        });*/

//方法引用
//创建对象:
Test03 t=new Test03();
list.stream()
        .filter(t::method)
        .forEach(s -> System.out.println(s));//张无忌
    }
    
    //已放入本类
    public boolean method(String s) {
        return s.startsWith("张")&&s.length()==3;
    }
    
}

这里同样满足四个要求:

  1. 引用处必须是函数时接口
  2. 被引用的方法已存在
  3. 被引用的方法的形参和返回值和抽象方法一样
  4. 功能满足需求

疑问:
不是说引用本类成员方法的格式是 this::方法名 吗
是的,但是本例子中的引用处是main方法,是静态的,而以前说过静态内没有this关键字,所以还是要创建本类对象再去用 对象名::方法名


引用父类的成员方法(引用处不能是静态)

格式:super::方法名

举例:
定义一个接口:Greetable


public interface Greetable {
    void greet();//问候的抽象方法
}

Person类


public class Person {
    //问候方法
    public void sayHello(){
        System.out.println("Hello! 我是人。");
    }
}

子类Teacher

public class Teacher extends Person {
    //重写父类方法
    @Override
    public void sayHello() {
        System.out.println("Hello!,我是老师");
    }


    //定义一个方法,参数传递Gerrtable接口
    public void method(Greetable g) {
        g.greet();
    }


    public void show() {
        //调用method,
        //以前:使用匿名内部类的写法,调用父类的打招呼方法
         method(new Greetable() {
            @Override
            public void greet() {
                Teacher.super.sayHello();
            }
        });
        //现在:方法引用
        method(super::sayHello);
    }
}

测试类:

class Test {
    public static void main(String[] args) {
        Teacher t = new Teacher();
        t.show();
    }
}

Hello! 我是人。
Hello! 我是人。


引用构造方法

格式:** 类名**::new
如:Student::new

练习:
image.png
Student类:

public class Student {
    //属性
  private String name;
  private int age;

    //构造
    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
// ...set、get、toString
}

测试类:

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

//创建集合
ArrayList<String> list = new ArrayList<>();
//添加元素
Collections.addAll(list, "张三,23", "李四,24", "王五,25");
//普通写法
/*  List<Student> newList = list.stream().map(new Function<String, Student>() {
    @Override
    public Student apply(String s) {
        return new Student(s.split(",")[0], Integer.parseInt(s.split(",")[1]));
    }
}).collect(Collectors.toList());*/

    //方法引用
    list.stream().map(Student::new);
    }
}



当我们在测试类中写到上面这一步时,new会报错,也就是说肯定是违背了方法引用的规则
image.png
可以发现抽象方法中只有一个形参,
而Student的构造方法只有无参或两个参,
这时我们就可以手动在Student中写一个只有一个形参的构造方法
如:
Student

public class Student {
    //属性
  private String name;
  private int age;

    //构造
    public Student() {
    }

    //其中str就表示流中的每个数据
  //调用构造方法时不用考虑返回值,因为该引用构造后一定会返回是否和抽象方法一致即可
    public Student(String str){
        this.name=str.split(",")[0];
        this.age=Integer.parseInt(str.split(",")[1]);
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
// ...set、get、toString
}
public class Test04 {
    public static void main(String[] args) {

//创建集合
ArrayList<String> list = new ArrayList<>();
//添加元素
Collections.addAll(list, "张三,23", "李四,24", "王五,25");
        
//普通写法
/*  List<Student> newList = list.stream()
    .map(new Function<String, Student>() {
        @Override
        public Student apply(String s) {
        return new Student(s.split(",")[0], Integer.parseInt(s.split(",")[1]));
        }
}).collect(Collectors.toList());*/

        
//方法引用
List<Student> newList = list.stream()
        .map(Student::new)
        .collect(Collectors.toList());
System.out.println(newList);
//[Student{name = 张三, age = 23}, Student{name = 李四, age = 24}, Student{name = 王五, age = 25}]
    }
}

最后来看看Collectors的toList方法底层逻辑:
image.png
原来toList方法底层也是使用方法引用创建的ArrayList的对象
image.png

**其他方法引用的使用:

*使用类名引用成员方法(特殊):

格式:类名::成员方法
如:String::substring

练习:
image.png

//创建集合
ArrayList<String>list=new ArrayList<>();
Collections.addAll(list,"a","b");
//普通写法
//String->String,可以使用map方法
/*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. 函数式接口
  2. 被引用方法已存在
  3. *被引用方法的形参,需要跟抽象方法的第二个形参到最后一个形参一致,返回值要一致
    1. 第一个参数:表示被引用方法的调定者,决定了可以引用哪些类中的方法
      1. 在Stream流当中,第一个参数一般都表示流里面的每一个数据。假设流里面的数据是字符串,那么使用这种方式进行方法引用,只能引用String这个类的方法
    2. 第二个参数到最后一个参数:跟抽象方法的形参保持一致,如果抽象方法没有第二个参数,说明引用的方法需要是无参的成员方法。
  4. 被引用方法的功能需要满足当前的需求

如:
上面的抽象方法apply的形参是String类型,且只有一个,决定了只能引用String
类中的无参方法,还要满足需求,就只有toUpperCase这个方法
image.png

这种使用类名引用的局限性:
不能引用所有类中的成员方法。
是跟抽象方法的第一个参数有关,这个参数是什么类型的,那么就只能引用这个类中的方法。


引用数组的构造方法

格式:数据类型[ ] : :new
如:int [ ] : : new

练习:
image.png

ArrayList<Integer>list=new ArrayList<>();
Collections.addAll(list,1,2,3);
//普通表示
/*Integer[] arr = list.stream().toArray(value -> new Integer[value]);
        System.out.println(Arrays.toString(arr));*/

//方法引用
Integer[] arr = list.stream().toArray(Integer[]::new);
System.out.println(Arrays.toString(arr));//[1, 2, 3]

总结:
Snipaste_2024-02-03_13-10-52.png

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

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

相关文章

SpringBoot整合ElasticSearch7.6.2入门

一、前期准备 ElasticSearch作为基于Lucene的一款分布式全文检索服务器&#xff0c;可以通过暴露restfulAPI来操作索引、搜索&#xff0c;具备有实时搜索、稳定、可靠、快速、安装使用方便等特点&#xff0c;是目前使用最广泛的企业级搜索引擎。 本期的目的就是——在springBoo…

前端工程化之:webpack1-13(内置插件)

目录 一、内置插件 1.DefinePlugin 2.BannerPlugin 3.ProvidePlugin 一、内置插件 所有的 webpack 内置插件都作为 webpack 的静态属性存在的&#xff0c;使用下面的方式即可创建一个插件对象&#xff1a; const webpack require("webpack")new webpack.插件…

线性表 —— 链表

与数组对比 ◼ 数组&#xff1a;  要存储多个元素&#xff0c;数组&#xff08;或选择链表&#xff09;可能是最常用的数据结构。  我们之前说过&#xff0c;几乎每一种编程语言都有默认实现数组结构。 ◼ 但是数组也有很多缺点&#xff1a;  数组的创建通常需要申请一…

【EVP】Explicit Visual Prompting for Low-Level Structure Segmentations

目录 &#x1f347;&#x1f347;0.简介 &#x1f337;&#x1f337;1.研究动机 &#x1f34b;&#x1f34b;2.主要贡献 &#x1f353;&#x1f353;3.网络结构 &#x1f36d;3.1整体结构 &#x1f36d;3.2高频分量计算 &#x1f36d;3.3显示视觉提示EVP &#x1f342;&…

数学电路与电子工程2(MEE)—— 时序电路的寄存器和工作频率

1. 基本的数字逻辑存储元件&#xff1a;D锁存器和D触发器 D锁存器&#xff08;Verrou ou D latch&#xff09;&#xff0c;它是一个简单的存储设备&#xff0c;可以在使能信号&#xff08;E&#xff09;处于活动状态时存储一位数据。当E为高电平时&#xff0c;D锁存器的输出Q会…

React | Center 组件

在 Flutter 中有 Center 组件&#xff0c;效果就是让子组件整体居中&#xff0c;挺好用。 React 中虽然没有对应的组件&#xff0c;但是可以简单封装一个&#xff1a; index.less .container {display: flex;justify-content: center;align-items: center;align-content: ce…

java设计模式:策略模式

在平常的开发工作中&#xff0c;经常会用到不同的设计模式&#xff0c;合理的使用设计模式&#xff0c;可以提高开发效率&#xff0c;提高代码质量&#xff0c;提高代码的可拓展性和维护性。今天来聊聊策略模式。 策略模式是一种行为型设计模式&#xff0c;运行时可以根据需求动…

Stable diffusion使用和操作流程

Stable Diffusion是一个文本到图像的潜在扩散模型,由CompVis、Stability AI和LAION的研究人员和工程师创建。它使用来自LAION-5B数据库子集的512x512图像进行训练。使用这个模型,可以生成包括人脸在内的任何图像,因为有开源的预训练模型,所以我们也可以在自己的机器上运行它…

Windows下MySQL的界面安装

华子目录 下载MySQL安装MySQL配置MySQL配置环境变量检验MySQL是否安装成功 下载MySQL 首先我们在MySQL的官方下载MySQL https://www.mysql.com 点击download&#xff0c;开始下载 安装MySQL 下载完成后&#xff0c;我们双击msi文件 再点击next 之后我们先勾选I acc…

leetcode 1.两数之和(C++)DAY1(待补充哈希表法)

文章目录 1.题目描述示例提示 2.解答思路3.实现代码结果4.总结 1.题目描述 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是&…

堪称灾难级攻击的 UDP FLOOD洪水攻击,应该如何防护?

DDOS又称为分布式拒绝服务&#xff0c;全称是Distributed Denial of Service。DDOS本是利用合理的请求造成资源过载&#xff0c;导致服务不可用&#xff0c;从而造成服务器拒绝正常流量服务。就如酒店里的房间是有固定的数量的&#xff0c;比如一个酒店有50个房间&#xff0c;当…

Java语法学习线程基础

Java语法学习线程基础 大纲 概念创建线程线程终止常用方法用户线程和守护线程线程的七大状态线程的同步互斥锁线程死锁释放锁 具体案例 1.概念 2. 创建线程 第一种&#xff1a; class Cat extends Thread {int time 0;Overridepublic void run() {while (true) {System.o…

计算机网络_1.5 计算机网络的性能指标

1.5 计算机网络的性能指标 一、总览二、常用的八个计算机网络性能指标1、速率&#xff08;1&#xff09;数据量&#xff08;2&#xff09;速率&#xff08;3&#xff09;数据量与速率中K、M、G、T的数值辨析&#xff08;4&#xff09;【练习1】计算发送数据块的所需时间 2、带宽…

C++ OpenGL绘制三维立体skybox场景obj模型AABB碰撞检测旋转动画界面

程序示例精选 C OpenGL绘制三维立体skybox场景obj模型AABB碰撞检测旋转动画界面 如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对《C OpenGL绘制三维立体skybox场景obj模型AABB碰撞检测旋转动…

使用Ettus USRP X440对雷达和EW系统进行原型验证

概览 无论是保障己方平台的生存能力&#xff0c;还是扰乱敌方频谱使用&#xff0c;以电磁(EM)频谱为主导都是任务成功的主要因素。电磁频谱操作(Electromagnetic Spectrum Operation, EMSO)需要使用战术系统来监测敌方的频谱活动、定位其发射器并帮助己方制定行动计划。软件无…

存算一体:架构创新,打破算力极限

1 需求背景 在全球数据量呈指数级暴涨&#xff0c;算力相对于AI运算供不应求的现状下&#xff0c;存算一体技术主要解决了高算力带来的高能耗成本矛盾问题&#xff0c;有望实现降低一个数量级的单位算力能耗&#xff0c;在功耗敏感的百亿级AIoT设备上、高能耗的数据中心、自动驾…

VSCode 安装LLDB调试器(OS X)并启动调试

插件&#xff1a;&#xff08;LLDB插件安装&#xff09; 安装这个版本不好弄错了&#xff0c;CodeLLDB&#xff08;名字&#xff09; 配置&#xff1a;&#xff08;LLDB启动调试&#xff09; {// 使用 IntelliSense 了解相关属性。 // 悬停以查看现有属性的描述。// 欲了解更…

阻塞队列(超详细易懂)

目录 一、阻塞队列 1.阻塞队列概述 2.生产者消费者模型 3.阻塞队列的作用 4.标准库中的阻塞队列类 5.例子&#xff1a;简单生产者消费者模型 二、阻塞队列模拟实现 1.实现循环队列&#xff08;可跳过&#xff09; 1.1简述环形队列 1.2代码实现 2.实现阻塞队列 2.1实…

CMake生成osg的FFMPEG插件及Windows下不生成VS工程问题解决

在Windows下&#xff0c;如何利用CMake生成osg的FFMPEG插件&#xff0c;请参考如下博文&#xff0c;同生成jpeg插件类似&#xff1a; osg第三方插件的编译方法&#xff08;以jpeg插件来讲解&#xff09;。 如下为生成FFMPEG时必要的设置&#xff1a; 注意&#xff1a; 一定要…

开发智能化企业培训平台:教育系统源码的创新方法

在传统的企业培训模式中&#xff0c;往往面临着效率低下、内容过时以及难以个性化的问题。为了解决这些挑战&#xff0c;采用智能化技术成为了企业培训领域的热门趋势。通过开发智能化企业培训平台&#xff0c;可以提高培训效果、降低成本&#xff0c;并更好地满足员工多样化的…