一、概念
- 函数式接口的实现除了有Lambda表达式外,还有方法引用、构造器引用和数组引用的实现;
- 方法引用可以看做是Lambda表达式深层次的表达。换句话说,方法引用就是Lambda表达式,也就是函数式接口的一个对象(实例);
- 方法引用是一种引用方法的轻量级语法;
Labmda表达式是函数式接口中抽象方法的实现
如果已经存在一个实现了Lambda表达式的方法,那么就不需要编写Lambda表达式,而直接使用这个已经存在的方法,这称之为方法引用
方法引用:引用(使用)已经存在的方法
Lambda表达式用于实现函数式接口中抽象方法 - 方法
如果已经存在某个方法可以实现函数式接口中的抽象方法,那么我们就不需要定义Lambda表达式,而直接引用已经存在的方法即可。
二、使用场景
在Lambda表达式中,如果Lambda体的操作,已经有实现的方法了,那么就可以使用方法引用。
三、要求
函数式接口的抽象方法的参数列表,必须与方法引用的方法的参数列表保持一致,而返回值类型原则上也需要保持一致。
- 如果抽象方法的返回值为void,而方法引用的方法有返回值,则忽略;
- 抽象方法的返回值类型(如:double)必须能兼容方法引用的方法返回值类型(如:int)。
1 2 | Comparator<Integer> comparator = (x,y) -> Integer.compare(x,y) ; Comparator<Integer> comparator = Integer::compare; |
四、语法
- 使用操作符 “::” 将类(或对象)与方法名分割开来(是已经实现好的方法名及其所在的类或对象);
- 可以看成左边的类或对象是调用者,右边的方法是被调用者。
以下是六种不同类型的方法引用:
类型 | 例子 |
---|---|
静态方法的引用 | 类名::静态方法 |
成员方法的引用 | 对象::成员方法 |
特定类型方法的引用 | 类名::成员方法 |
构造器引用 | 类名::new |
数组引用 | 数据类型[]::new |
父类方法引用、本类方法引用 | super::成员方法 、this::成员方法 |
五、案例
1、成员方法的引用
2、静态方法的引用
第一:定义函数式接口
1 2 3 4 5 6 7 8 9 10 | /** * 问候接口 - 函数式接口 * * @Date 2023-04-07 * @Author zqx */ @FunctionalInterface public interface IWenHou { void sayHello(String name); } |
第二:用户对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | /** * 用户 * * @Date 2023-04-07 * @Author zqx */ public class User { /** * 函数式接口作为方法参数的使用 * * @param name * @param iWenHou */ public void wenHao(String name, IWenHou iWenHou) { iWenHou.sayHello(name); } /** * 问候 - 早上好 - 成员方法 * * @param name */ public void goodMorning(String name) { System.out.println("早上好," + name); } /** * 问候 - 下午好 - 静态方法 * * @param name */ public static void goodAfternoon(String name) { System.out.println("下午好," + name); } /** * 问候 - 晚上好 - 静态方法 - 注意:此方法与函数式接口的方法不匹配 * * @return */ public static int goodNight() { System.out.println("晚安"); return 8; } } |
第三:测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | /** * 成员方法的引用、静态方法的引用 * * @Date 2023-04-07 * @Author zqx */ public class MainTest { public static void main(String[] args) { // 实例化用户对象 User user = new User() ; // 1.Lambda表达式 - void sayHello(String name); user.wenHao("李四",(name)->System.out.println("你好呀," + name)); // 2.方法引用 - 引用已经存在的方法,代替 Lambda 表达式,从而实现函数式接口中的抽象方法 // 2.1 引用静态方法 - 类名::静态方法 user.wenHao("王五",User::goodAfternoon); // 2.2 引用成员方法 - 对象名::成员方法 user.wenHao("赵六",user::goodMorning); // 错误 - 引用的方法不匹配函数式接口中的抽象方法 // user.wenHao("赵六",User::goodNight); } } |
3、父类方法引用
4、本类方法引用
第一:定义函数式接口
1 2 3 4 5 6 7 8 9 10 11 12 | /** * 函数式接口 - 移动接口 * * @Date 2023-04-07 * @Author zqx */ public interface IMove { /** * 移动 */ void move(); } |
第二:定义父类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | /** * 父类 * * @Date 2023-04-07 * @Author zqx */ public class Animal { /** * 跑 */ public void run() { System.out.println("跑"); } } |
第三:定义子类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | /** * 子类 * * @Date 2023-04-07 * @Author zqx */ public class Lion extends Animal { /** * 函数式接口作为方法参数的使用 - 移动行为 * * @param im */ public void move(IMove im) { im.move(); } /** * 吃 */ public void eat() { System.out.println("吃"); } /** * 捕食 - 动起来 - 调用 move 方法 * 1)匿名内部类实现 */ public void buShi1() { move(new IMove(){ @Override public void move() { Lion.super.run(); } }); } /** * 捕食 - 动起来 - 调用 move 方法 * 2)Lambda表达式实现 - void move(); */ public void buShi2() { move(()->{ super.run(); }) ; } /** * 捕食 - 动起来 - 调用 move 方法 * 3)方法引用 - void move(); */ public void buShi3() { // 引用父类的方法 - super::方法名称 move(super::run) ; } /** * 捕食 - 动起来 - 调用 move 方法 * 3)方法引用 - void move(); */ public void buShi4() { // 引用本类的方法 - this::方法名称 move(this::eat) ; } } |
第四:测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | /** * @Date 2023-04-07 * @Author zqx */ public class MainTest { public static void main(String[] args) { Lion xb = new Lion(); xb.buShi1(); xb.buShi2(); xb.buShi3(); xb.buShi4(); } } |
5、特定类型方法的引用
在Lambda表达式中,如果参数列表中的第一个参数是成员方法的调用者,而第二个参数是成员方法的参数时,则可以使用特定类型引用(类名::成员方法)
1 2 3 4 5 6 7 8 9 10 | // 第一个参数x是成员方法equals的调用者 // 第二个参数y是成员方法equals的参数 BiPredicate<String,String> bl1 = (x,y) -> x.equals(y) ; // 满足上面的条件,因此可以使用特定类型的方法引用 BiPredicate<String,String> bl2 = String::equals ; System.out.println(bl1.test("aa", "aa")); System.out.println(bl2.test("cc", "cc")); |
6、构造方法的引用
第一:定义学生对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | /** * 学生 * * @Date 2023-04-06 * @Author zqx */ public class Student { /** * 姓名 */ private String name; /** * 年龄 */ private int age; public Student() { } public Student(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return this.name + "-" + this.age; } } |
第二:定义函数式接口
1 2 3 4 5 6 7 8 9 10 | /** * 构造学生对象的工具类 - 基于 namt 和 age ,实例化 Student 对象,并返回 * * @Date 2023-04-07 * @Author zqx */ @FunctionalInterface public interface StudentBuilder { Student create(String name,int age) ; } |
第三:测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | /** * @Date 2023-04-07 * @Author zqx */ public class MainTest { /** * 构造学生对象 - 函数式接口作为方法参数的使用 * * @param name * @param age * @param sb * @return */ private static Student build(String name, int age, StudentBuilder sb) { return sb.create(name, age); } public static void main(String[] args) { // 1.匿名内部类 Student stu1 = build("张三", 18, new StudentBuilder() { @Override public Student create(String name, int age) { // 调用构造方法 return new Student(name,age); } }); System.out.println(stu1); // 2.Lambda表达式实现 - Student create(String name,int age) ; Student stu2 = build("李四",28,(name,age)->new Student(name,age)) ; System.out.println(stu2); // 3.构造方法的引用 - 类名::new Student stu3 = build("王五",38,Student::new) ; System.out.println(stu3); } } |
7、数组引用
第一:定义函数式接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | /** * 创建指定类型的数组的工具类 * * @Date 2023-04-07 * @Author zqx */ @FunctionalInterface public interface ArrayBuilder<T> { /** * 创建一个指定长度T类型的数组 * * @param len * @return */ T[] create(int len); } |
第二:测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | /** * 测试 * * @Date 2023-04-07 * @Author zqx */ public class MainTest { /** * 构造一个指定长度的字符串数组 * * @param len * @param ab */ public static String[] build(int len, ArrayBuilder<String> ab) { return ab.create(len); } public static void main(String[] args) { // 1.通过匿名内部类实现 String[] arr1 = build(3,new ArrayBuilder<String>(){ @Override public String[] create(int len) { return new String[len]; } }) ; arr1[0] = "aa" ; arr1[1] = "bb" ; arr1[2] = "cc" ; System.out.println(Arrays.toString(arr1)); // 2.通过 Lambda表达式 实现 - T[] create(int len); String[] arr2 = build(3,(len)->new String[len]) ; arr2[0] = "ee" ; arr2[1] = "ff" ; arr2[2] = "gg" ; System.out.println(Arrays.toString(arr2)); // 3.通过 方法引用 实现 - 数据类型[]::new String[] arr3 = build(3,String[]::new) ; arr3[0] = "hh" ; arr3[1] = "ii" ; arr3[2] = "jj" ; System.out.println(Arrays.toString(arr3)); } } |