Java(方法引用)
一、方法引用
1.方法引用的出现原因
在使用Lambda表达式的时候,我们实际上传递进去的代码就是一种解决方案:拿参数做操作
那么考虑一种情况:如果我们在Lambda中所指定的操作方案,已经有地方存在相同方案,那是否还有必要再写重复逻辑呢?答案肯定是没有必要
那我们又是如何使用已经存在的方案的呢?
这就是我们要讲解的方法引用,我们是通过方法引用来使用已经存在的方案
2.方法引用符
-
方法引用符
:: 该符号为引用运算符,而它所在的表达式被称为方法引用
-
推导与省略
- 如果使用Lambda,那么根据“可推导就是可省略”的原则,无需指定参数类型,也无需指定的重载形式,它们都将被自动推导
- 如果使用方法引用,也是同样可以根据上下文进行推导
- 方法引用是Lambda的孪生兄弟
3.方法引用的示例
1.需求:创建一个数组,进行倒序排列
- 1.1 匿名内部类
//需求:创建一个数组 进行倒序排列Integer[] arr = {3,5,4,1,6,2};
// 匿名内部类Arrays.sort(arr, new Comparator<Integer>() {@Overridepublic int compare(Integer o1, Integer o2) {return o2-o1;}});
- 1.2Lambda表达式
//lambda表达式//因为第二个参数的类型Comparator是一个函数式接口Arrays.sort(arr, (o1, o2) -> o2 - o1);
- 1.3方法引用
方法引用
1.引用处必须是函数式接口
2.被引用的方法必须是已经存在的
3.被引用的方法的参数和返回值要和函数式接口中抽象方法的参数和返回值保持一致
4.被引用的方法的功能需要满足当前的要求
Arrays.sort(arr, FunctionDemo1::substraction);System.out.println(Arrays.toString(arr));
public static int substraction(int num1, int num2) {return num2 - num1;}
4.引用静态类方法
引用类方法,其实就是引用类的静态方法
-
格式
类名::静态方法
-
范例
Integer::parseInt
Integer类的方法:public static int parseInt(String s) 将此String转换为int类型数据
public class FunctionDemo2 {public static void main(String[] args) {/*** 方法引用:* 引用处必须是函数式接口* 被引用的方法必须是已经存在的* 被引用的方法的参数和返回值要和函数式接口中抽象方法的参数和返回值保持一致* 被引用的方法的功能需要满足当前的要求*//**方法引用(引用静态方法)格式类::方法名需求:集合中有以下数字,要求把他们变成int类型"1","2","3","4","5","6"*///1.创建集合并添加元素ArrayList<String> list = new ArrayList<>();Collections.addAll(list,"1","2","3","4","5","6");// list.stream().map(new Function<String, Integer>() {
// @Override
// public Integer apply(String s) {
// return Integer.parseInt(s);
// }
// }).forEach(s-> System.out.println(s));//2.方法引用list.stream().map(Integer::parseInt).forEach(s-> System.out.println(s));}}
5.引用成员方法
5.1其他类::方法类
public class FunctionDemo3 {public static void main(String[] args) {/*方法引用(引用成员方法)格式其他类:其他类对象::方法名本类: 本类对象::方法名父类:父类对象::方法名需求:集合中有一些 名字,按照要求过滤数据数据:"张无忌","周芷若","张强","张三丰"要求:1.过滤出姓张的2.过滤出姓名长度为3的*/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()::checkString).forEach(s-> System.out.println(s));//
// StringOperation so = new StringOperation();// list.stream().filter(so::checkString).forEach(s-> System.out.println(s));//this 不能在静态方法中使用list.stream().filter(FunctionDemo3::checkString).forEach(s-> System.out.println(s));}public static boolean checkString(String s){return s.startsWith("张") && s.length() == 3;}}
- StringOperation.class:
public class StringOperation {public boolean checkString(String s){return s.startsWith("张") && s.length() == 3;}
}
- 注意在静态方法中 不能使用this::方法名,所以在这个方法中只能使用类名::方法名
5.2引用本类或父类的成员方法
- 本类: 本类对象::方法名
- 父类:父类对象::方法名
6.引用构造方法
public class FunctionDemo4 {public static void main(String[] args) {/*方法引用(引用构造方法)格式类名::new目的:创建这个类的对象需求:集合里面存储姓名和年龄,要求封装成Student对象并收集到List集合中方法引用的规则:1.需要有函数式接口2.被引用的方法必须已经存在3.被引用方法的形参和返回值,需要跟抽象方法的形参返回值保持一致4.被引用方法的功能需要满足当前的需求*///1.创建集合对象ArrayList<String> list = new ArrayList<>();//2.添加元素Collections.addAll(list,"张无忌,15","周芷若,16","张强,17","张三丰,18");//3.封装成Student对象并收集到List集合中
// String --> Student// List<Student> collect = list.stream().map(new Function<String, Student>() {
// @Override
// public Student apply(String s) {
// String[] arr = s.split(",");
// String name = arr[0];
// int age = Integer.parseInt(arr[1]);
// Student stu = new Student(name, age);
// return stu;
// }
// }).collect(Collectors.toList());
// System.out.println(collect);List<Student> newlist = list.stream().map(Student::new).collect(Collectors.toList());System.out.println(newlist);}
}
Student.class:
package com.itheima.a01myfuction;public class Student {private String name;private int age;public Student() {}public Student(String str) {String[] arr = str.split(",");this.name = arr[0];this.age = Integer.parseInt(arr[1]);}public Student(String name, int age) {this.name = name;this.age = age;}/*** 获取* @return name*/public String getName() {return name;}/*** 设置* @param name*/public void setName(String name) {this.name = name;}/*** 获取* @return age*/public int getAge() {return age;}/*** 设置* @param age*/public void setAge(int age) {this.age = age;}public String toString() {return "Student{name = " + name + ", age = " + age + "}";}
}
7.类名引用成员方法
public class FunctionDemo5 {public static void main(String[] args) {/**方法引用(类名引用成员方法)格式类名::成员方法需求:集合里面一些字符串,要求变成大写后进行输出方法引用的规则:1.需要有函数式接口2.被引用的方法必须已经存在3.被引用方法的形参,需要跟抽象方法的第二个形参到最后一个形参保持一致,返回值需要保持一致。4.被引用方法的功能需要满足当前的需求抽象方法形参的详解:第一个参数:表示被引用方法的调用者,决定了可以引用哪些类中的方法在Stream流当中,第一个参数一般都表示流里面的每一个数据。假设流里面的数据是字符串,那么使用这种方式进行方法引用,只能引用String这个类中的方法第二个参数到最后一个参数:跟被引用方法的形参保持一致,如果没有第二个参数,说明被引用的方法需要是无参的成员方法局限性:不能引用所有类中的成员方法。是跟抽象方法的第一个参数有关,这个参数是什么类型的,那么就只能引用这个类中的方法。*///1.创建集合对象ArrayList<String> list = new ArrayList<>();Collections.addAll(list,"a","b","c","d");// list.stream().map(new Function<String, String>() {
// @Override
// public String apply(String s) {
// return s.toUpperCase();
// }
// }).forEach(s-> System.out.println(s));//map(String::toUpperCase)//拿着流里面的每一个数据 去调用String类中的toUpperCase方法list.stream().map(String::toUpperCase).forEach(s-> System.out.println(s));}
}
8.引用数组的构造方法
public class FunctionDemo6 {public static void main(String[] args) {/*方法引用(数组的构造方法)格式数据类型[]::new目的:创建一个指定类型的数组需求:集合中存储一些整数,收集到数组当中细节:数组的类型,需要跟流中数据的类型保持一致。*///1.创建集合并添加元素ArrayList<Integer> list = new ArrayList<>();Collections.addAll(list,1,2,3,4,5,6,7,8,9);// Integer[] array = list.stream().toArray(new IntFunction<Integer[]>() {
// @Override
// public Integer[] apply(int value) {
// return new Integer[]{value};
// }
// });Integer[] array = list.stream().toArray(Integer[]::new);System.out.println(Arrays.toString(array));}
}
