JAVA方法引用:

  • 方法引用的出现原因在使用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>() {@Overridepublic 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>() {@Overridepublic 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>() {@Overridepublic 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>() {@Overridepublic 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 {//重写父类方法@Overridepublic void sayHello() {System.out.println("Hello!,我是老师");}//定义一个方法,参数传递Gerrtable接口public void method(Greetable g) {g.greet();}public void show() {//调用method,//以前:使用匿名内部类的写法,调用父类的打招呼方法method(new Greetable() {@Overridepublic 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>() {@Overridepublic 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>() {@Overridepublic 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>() {@Overridepublic 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.hqwc.cn/news/450582.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

threejs——多重场景渲染

前言 摸鱼时发现了这threejs实现的效果&#xff0c;从效果图中可以看出来&#xff0c;在滚动页面的时候&#xff0c;模型在进入不同的场景&#xff0c;或者说进入不同的页面&#xff0c;渲染模式改变了&#xff0c;下面我们一步一步拆解出这种效果是怎么实现的&#xff0c;首先…

数据集标注工具anylabeling解析

最近帮助其他课题组的学姐标注数据集&#xff0c;课题组使用的是anylabeling软件&#xff0c;相比于其他数据标注软件&#xff0c;例如labelme等&#xff0c;anylabeling软件使用时可以选择不同的模型&#xff0c;可以做到在图片上点几个点的轮廓&#xff0c;模型将自动识别出大…

人脸数据集:The Database of Faces (ATT)

参考来源: To evaluate the protection provided by our constructions against inference attacks, we use the AT&T Faces data set [17]. It consists of a total of 400 tightly cropped face images, showing 10 different angles for each of 40 human subjects. E…

在Linux中如何理解页表和进程地址

1、进程地址是进程读取资源的窗口 2、页表决定了进程真实拥有的资源情况 3、合理的对进程地址空间页表进行资源划分&#xff0c;就可以对进程的资源进行分类 这个过程应该如何去理解呢请看下面的图 我们知道程序被加载到进程中&#xff0c;会产生相应的PCB&#xff0c;并且…

css新手教程

css新手教程 课程&#xff1a;14、盒子模型及边框使用_哔哩哔哩_bilibili 一.什么是CSS 1.什么是CSS Cascading Style Sheet 层叠样式表。 CSS&#xff1a;表现&#xff08;美化网页&#xff09; 字体&#xff0c;颜色&#xff0c;边距&#xff0c;高度&#xff0c;宽度&am…

通俗易懂理解通道注意力机制(CAM)与空间注意力机制(SAM)

重要说明&#xff1a;本文从网上资料整理而来&#xff0c;仅记录博主学习相关知识点的过程&#xff0c;侵删。 一、参考资料 通道注意力&#xff0c;空间注意力&#xff0c;像素注意力 通道注意力机制和空间注意力机制 视觉 注意力机制——通道注意力、空间注意力、自注意力…

Shell脚本监控进程异常终止并重启

首先介绍一下我现在的需求&#xff0c;我服务器上挂了一个用python编写的kook机器人&#xff0c;但有时候机器人程序会异常终止&#xff0c;不知道什么原因&#xff0c;因此需要监控机器人程序是否有在运行&#xff0c;如果没有就重启机器人程序。 可以写一个Shell脚本&#x…

Excel中将16进制数转化成10进制(有/无符号)

Excel中将16进制数转化成10进制&#xff08;有/无符号&#xff09; Excel或者matlab中常用XXX2XXX进行不同进制的转换 16进制转10进制&#xff08;无符号数&#xff09;&#xff1a;HEX2DEC 16进制转10进制&#xff08;有符号数&#xff09;&#xff1a; FA46为例&#xff0c…

如何彻底卸载MySQL【可以解决问题】

[序]写在前面 相信很多小伙伴都遇到了以前版本的MySQL没有卸载干净而导致新版本的MySQL无法安装的情况&#xff0c;今天小编带你彻底解决这个令人头痛的问题&#xff08;本人也有亲身经历&#xff01;希望能够给大家带来一点点帮助&#xff09; 注&#xff1a;本文部分图片来自…

shell脚本中的变量,运算符

1.脚本格式 我们一般将shell脚本写在xxx.sh文件中&#xff0c;执行的时候bash/sh xxx.sh 注意文件路径 xxx.sh文件中的第一行为 #!/usr/bin/bash 注代表我们使用的是bin文件夹下的bash解释器(此条为注释语句&#xff0c;不写也可以) 2.echo用法 相当与print 示例1&…

《国色芳华》爆红网络,杨紫的“唐妆”惊艳四座。

♥ 为方便您进行讨论和分享&#xff0c;同时也为能带给您不一样的参与感。请您在阅读本文之前&#xff0c;点击一下“关注”&#xff0c;非常感谢您的支持&#xff01; 文 |猴哥聊娱乐 编 辑|徐 婷 校 对|侯欢庭 在中国的电视剧市场近几年的趋势中&#xff0c;仙侠剧的热度逐…

Blender使用Rigify和Game Rig Tool基础

做动画需要的几个简要步骤&#xff1a; 1.建模 2.绑定骨骼 3.绘制权重 4.动画 有一个免费的插件可以处理好给引擎用&#xff1a;Game Rig Tool 3.6和4.0版本的 百度网盘 提取码&#xff1a;vju8 1.Rigify是干嘛用的&#xff1f; 》 绑定骨骼 2.Game Rig Tool干嘛用的&#xf…