🍁1. 方法引用
方法的引用:把已经存在的方法拿来使用,当作函数式接口中抽象方法的方法体
" :: "是方法引用符
方法引用时需要注意:
1. 需要有函数式接口
2. 被引用的方法必须存在
3. 被引用的方法的形参和返回值要和抽象方法保持一致
4. 被引用的方法的功能要满足当前的需求
以Arrays中的静态方法 sort() 为例,其中的参数就是一个函数式接口,先来用匿名内部类和lambda的方式演示一下
需求:把数组中的元素降序排列
public class Demo1 { public static void main(String[] args) { //把数组中的内容倒序排列 Integer[] arr = {2, 1, 4, 5, 3}; //匿名内部类的方式 Arrays.sort(arr, new Comparator<Integer>() { public int compare(Integer o1, Integer o2) { return o2 - o1; } }); // lambda表达式 Arrays.sort(arr, (Integer o1, Integer o2) -> { return o2 - o1; }); // lambda表达式简化版本 Arrays.sort(arr, (o1, o2) -> o2 - o1); System.out.println(Arrays.toString(arr)); } }
接下来看方法引用的方式
public class Demo1 { public static void main(String[] args) { //把数组中的内容倒序排列 Integer[] arr = {2, 1, 4, 5, 3}; //方法引用 Arrays.sort(arr, Demo1::subtraction); System.out.println(Arrays.toString(arr)); } public static int subtraction(int num1, int num2) { return num2 - num1; } }
🍁2. 方法引用的分类
🍁2.1 引用静态方法
格式: 类 :: 方法名
需求:获取集合中的数字部分
如果说使用之前的方法来解决,就是再定义一个集合,在循环中调用parseInt()方法,再把结果添加到新集合中
public class Demo2 { public static void main(String[] args) { ArrayList<String> arrayList = new ArrayList<>(); Collections.addAll(arrayList, "1", "2", "3", "4"); //匿名内部类方式 arrayList.stream().map(new Function<String, Integer>() { public Integer apply(String s) { int res = Integer.parseInt(s); return res; } }).forEach(s -> System.out.print(s)); System.out.println(); //方法引用 arrayList.stream().map(Integer::parseInt).forEach(s -> System.out.print(s)); } }
这里正好符合方法引用的条件
🍁2.2 引用成员方法
格式:
其他类:其他类的对象 :: 方法名
本类中:this :: 方法名
父类中:super :: 方法名
需求:找出集合中以 'a' 开头并且长度大于2的字符串
这里在另一个类中定义了一个专门进行字符串判断的方法,并且参数类型和返回值都和用到的抽象方法一致,这时就可以使用方法引用了
🍁2.3 引用构造方法
格式:类名 :: new
需求:把集合中的字符串封装成Student对象收集到List集合中
public class Student { private String name; private int age; public Student() { } public Student(String name, int age) { this.name = name; this.age = age; } public Student(String s) { this.name = s.split(",")[0]; this.age = Integer.parseInt(s.split(",")[1]); } public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
public class Demo4 { public static void main(String[] args) { ArrayList<String> arrayList = new ArrayList<>(); Collections.addAll(arrayList, "张三,23", "李四,21", "王五,21"); //把集合中的字符串封装成Student对象收集到List集合中 //之前stream流的写法 List<Student> list1 = arrayList.stream().map(new Function<String, Student>() { public Student apply(String s) { return new Student(s.split(",")[0], Integer.parseInt(s.split(",")[1])); } }).collect(Collectors.toList()); System.out.println(list1); //引用构造方法 List<Student> list2 = arrayList.stream().map(Student::new).collect(Collectors.toList()); System.out.println(list2); } }
在使用引用构造方法的时候,由于需要保证被引用的方法的形参和返回值要和抽象方法保持一致,所以要重新再写一个符合条件的构造方法才可以
🍁3. 使用类名引用成员方法
这里的规则是要稍微变一下的,在刚开始提到的第三条中 “被引用的方法的形参和返回值要和抽象方法保持一致” 要更改为被引用方法的形参要和抽象方法的第二个参数到最后一个形参保持一致,返回值需要保持一致
对于抽象方法中的形参:第一个参数表示被引用方法的调用者,决定了可以引用哪些类中的方法,在Stream流中,第一个参数一般都表示流里面的每一个数据,如果说流里面的是字符串类型,那么使用这种方式进行方法引用,只能用String这个类中的方法
第二个参数到最后一个参数,需要和引用方法的形参保持一致,如果没有第二个参数,说明被引用的方法需要是无参的成员方法
public class Demo5 { public static void main(String[] args) { ArrayList<String> arrayList = new ArrayList<>(); Collections.addAll(arrayList, "aaa", "bbb", "ccc", "ddd"); //把集合中的字符串变为大写之后进行输出 arrayList.stream().map(new Function<String, String>() { public String apply(String s) { return s.toUpperCase(); } }).forEach(s -> System.out.print(s + " ")); System.out.println(); //方法引用 arrayList.stream().map(String::toUpperCase).forEach(s -> System.out.print(s + " ")); } }
这里抽象方法的方法体只有一个参数,引用的方法需要是无参的成员方法
🍁4. 引用数组的构造方法
引用数组的构造方法就是为了创建一个数组,创建的数组类型要和流中的数据类型保持一致
格式:数据类型[ ] :: new
需求:把集合中的数据收集起来放在数组中
public class Demo6 { public static void main(String[] args) { ArrayList<Integer> arrayList = new ArrayList<>(); Collections.addAll(arrayList,1,2,3,4,5); //匿名内部类方式 Integer[] array1 = arrayList.stream().toArray(new IntFunction<Integer[]>() { public Integer[] apply(int value) { return new Integer[value]; } }); System.out.println(Arrays.toString(array1)); //数组引用构造方法 Integer[] array2 = arrayList.stream().toArray(Integer[]::new); System.out.println(Arrays.toString(array2)); } }
🍁5. 方法引用的优点
简洁性:方法引用通常比Lambda表达式更简洁,特别是当Lambda表达式只是简单地调用一个已存在的方法时。
可读性:对于熟悉的方法名,方法引用比Lambda表达式更容易理解。
减少样本模板:方法引用减少了编写Lambda表达式时的样板代码,使代码更加清晰。