1 方法引用
1.1 方法引用简述
Lambda表达式可以使得在调用那些已经拥有方法名的方法的代码更简洁、更容易理解。
Arrays.sort(arr, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1 - o2;
}
});
Lambda表达式简写后:
Arrays.sort(arr, Arrays.sort(arr, ((o1, o2) -> o1 - o1));
但有些情况下,我们用Lambda表达式仅仅是调用一些已经存在的方法,除了调用动作外,没有其他任何多余的动作。在这种情况下,我们倾向于通过方法名来调用它。
从Java8开始,我们可以使用方法引用进一步简写。
方法引用就是:把已经存在的方法当作抽象方法的方法体。
方法引用可以理解为Lambda表达式的另外一种表现形式。
假设现已经存在一个做减法的方法,它的形参和返回值和抽象方法compare
一致,且本质也是做两个数的减法。
public int subtraction(int n1, int n2) {
return n1 - n2;
}
我们可以通过方法引用来实现排序。
import java.util.Arrays;
public class sortDemo2 {
public static void main(String[] args) {
Integer[] arr = {4, 5, 1, 8, 6, 34, 52, 33, 9, 23};
//方法引用
Arrays.sort(arr, sortDemo2::subtraction);
System.out.println(Arrays.toString(arr));
// [1, 4, 5, 6, 8, 9, 23, 33, 34, 52]
}
//做减法的函数
public static int subtraction(int n1, int n2) {
return n1 - n2;
}
}
1.2 方法引用前提
- 引用处必须是
函数式接口
。上文中的Comparator接口就是函数式接口。 - 被引用的方法必须
已经存在
。 - 被引用的方法的形参和返回值必须跟抽象方法
保持一致
。 - 被引用的方法的功能必须能满足当前的需求 。
2 引用静态方法
格式: 类名::静态方法
案例: 要把String类型集合中的字符串变成int类型的整数。
使用匿名内部类:
import java.util.ArrayList;
import java.util.Collections;
import java.util.function.Function;
public class FunctionDemo1 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "1", "8", "22", "13", "9");
list.stream().map(new Function<String, Integer>() {
@Override
public Integer apply(String s) {
return Integer.parseInt(s);
}
}).forEach(i -> System.out.print(i + " ")); //1 8 22 13 9
}
}
使用方法引用简写
import java.util.ArrayList;
import java.util.Collections;
public class FunctionDemo1 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "1", "8", "22", "13", "9");
list.stream()
.map(Integer::parseInt)
.forEach(i -> System.out.print(i + " ")); //1 8 22 13 9
}
}
3 引用成员方法
引用类 | 格式 |
---|---|
本类 | this::方法名 |
父类类 | super::方法名 |
其他类 | 其他类的对象::方法名 |
3.1 引用本类
引用处不能是静态方法,因为静态方法中没有
this
,super
关键字
import java.util.ArrayList;
import java.util.Collections;
public class FunctionDemo3 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "1", "8", "22", "13", "9");
new FunctionDemo3().test(list); //运行结果: 22 13
}
public void test(ArrayList<String> list) {
//引用本类成员方法
list.stream().filter(this::screen)
.forEach(s -> System.out.println(s));
}
public boolean screen(String s) {
return s.length() >= 2;
}
}
3.2 引用父类
引用处不能是静态方法,因为静态方法中没有
this
,super
关键字
import java.util.ArrayList;
import java.util.Collections;
public class FunctionDemo4 extends FunctionDemo4Father {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "1", "8", "22", "13", "9");
new FunctionDemo3().test(list); //运行结果: 22 13
}
public void test(ArrayList<String> list) {
//引用父类成员方法
list.stream().filter(super::screen)
.forEach(s -> System.out.println(s));
}
}
//父类
class FunctionDemo4Father {
public boolean screen(String s) {
return s.length() >= 2;
}
}
3.3 引用其他类
案例: 筛选出集合中字符串长度大于等于2的字符串。
使用匿名内部类:
import java.util.ArrayList;
import java.util.Collections;
import java.util.function.Predicate;
public class FunctionDemo2 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "1", "8", "22", "13", "9");
list.stream().filter(new Predicate<String>() {
@Override
public boolean test(String s) {
return s.length() >= 2;
}
}).forEach(s -> System.out.println(s));
}
}
当恰巧有一个类中的一个方法可以满足方法引用的前提时:
//恰巧有StringOperation类中的screen方法可以满足方法引用的前提
public class StringOperation {
public boolean screen(String s) {
return s.length() >= 2;
}
}
使用方法引用简写
import java.util.ArrayList;
import java.util.Collections;
public class FunctionDemo2 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "1", "8", "22", "13", "9");
list.stream().filter(new StringOperation()::screen)
.forEach(s -> System.out.println(s)); //运行结果: 22 13
}
}
4 引用构造方法
格式: 类名::new
案例: 将字符串类型集合中数据封装成Person对象,并添加到List集合中。
采用匿名内部类实现:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
public class FunctionDemo5 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "张三-19", "李四五-20", "张五六-18", "王五-22");
List<Person> personList = list.stream().map(new Function<String, Person>() {
@Override
public Person apply(String s) {
String[] split = s.split("-");
return new Person(split[0], Integer.parseInt(split[1]));
}
}).collect(Collectors.toList());
System.out.println(personList);
//运行结果: [Person{name = 张三, age = 19}, Person{name = 李四五, age = 20}, Person{name = 张五六, age = 18}, Person{name = 王五, age = 22}]
}
}
//Person类
class Person {
private String name;
private int age;
//省略带全参、无参构造,set、get方法
}
采用方法引用实现:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
public class FunctionDemo5 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "张三-19", "李四五-20", "张五六-18", "王五-22");
List<Person> personList = list.stream()
.map(Person::new)
.collect(Collectors.toList());
System.out.println(personList);
//运行结果: [Person{name = 张三, age = 19}, Person{name = 李四五, age = 20}, Person{name = 张五六, age = 18}, Person{name = 王五, age = 22}]
}
}
class Person {
private String name;
private int age;
//在JavaBean类中添加满足方法引用前提的构造方法:
// 1.被引用的方法的形参和返回值必须跟抽象方法保持一致;
// 2.被引用的方法的功能必须能满足当前的需求。
public Person(String listStr) {
String[] split = listStr.split("-");
this.name = split[0];
this.age = Integer.parseInt(split[1]);
}
//省略带全参、无参构造,set、get方法
}
5 通过类名引用成员方法
5.1 通过类名引用成员方法的前提
- 引用处必须是
函数式接口
。 - 被引用的方法必须
已经存在
。 - 抽象方法的
第一个
形参决定了可以引用哪些类中的方法 。 - 被引用的方法的形参需要跟抽象方法的
第二个到最后一个
形参保持一致 。 - 返回值必须跟抽象方法
保持一致
。 - 被引用的方法的功能必须能满足当前的需求 。
抽象方法的形参补充:
第一个形参:表示被引用方法的调用者,决定了可以引用哪些类中的方法。在Stream流中,第一个参数一般都表示流里面的每一个数据。假设流里面的数据是字符串类型,那么使用这种方式进行方法引用,只能引用String类中的方法。
第二个到最后一个形参:需要与被引用方法的形参保持一致,如果没有第二个参数,说明被引用的方法需要是无参的成员方法。
局限性: 抽象方法的第一个参数是什么类型的,就只能引用这个类中的方法,其他类的方法不能引用。
5.2 案例及解读
: 将集合中的字符串转换成大写
采用匿名内部类实现:
import java.util.ArrayList;
import java.util.Collections;
import java.util.function.Function;
public class FunctionDemo6 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "aaa", "ccc", "eee");
list.stream().map(new Function<String, String>() {
@Override
public String apply(String s) {
return s.toUpperCase();
}
}).forEach(s -> System.out.println(s));
}
}
采用方法引用实现:
import java.util.ArrayList;
import java.util.Collections;
public class FunctionDemo6 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "aaa", "ccc", "eee");
list.stream()
.map(String::toUpperCase)
.forEach(s -> System.out.println(s));
}
}
解释: Function
接口中的 apply
方法没有第二个参数,说明被引用的方法需要是无参的成员方法。而 String
类中的方法 toUpperCase
是无参的,且与 apply
方法的返回值一致。
public String toUpperCase() {
return toUpperCase(Locale.getDefault());
}
map(String::toUpperCase)
可以理解为:让流中的每一个数据去调用 String
中的 toUpperCase
方法,该方法的返回值就是转换后的结果。
6 引用数组的构造方法
格式: 数据类型[]::new
//创建一个该数据类型的数组
案例: 将集合中的整数收集到数组当中。
采用匿名内部类实现:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.function.IntFunction;
public class FunctionDemo7 {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list, 1, 3, 5, 2, 4);
Integer[] array = list.stream().toArray(new IntFunction<Integer[]>() {
@Override
public Integer[] apply(int value) {
return new Integer[value];
}
});
System.out.println(Arrays.toString(array)); //[1, 3, 5, 2, 4]
}
}
采用方法引用实现:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
public class FunctionDemo7 {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list, 1, 3, 5, 2, 4);
//会自动创建一个Integer类型的,长度与集合长度一致的数组。
Integer[] array = list.stream().toArray(Integer[]::new);
System.out.println(Arrays.toString(array)); //[1, 3, 5, 2, 4]
}
}
7 综合应用
1.集合中存储字符串:如”张三,23“。将数据收集到Student类型的数组当中。
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
public class FunctionDemo1 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "张三,23", "李四,24", "王五,26");
Student[] studentArr = list.stream()
.map(Student::new)
.toArray(Student[]::new);
System.out.println(Arrays.toString(studentArr));
//[Student{name = 张三, age = 23}, Student{name = 李四, age = 24}, Student{name = 王五, age = 26}]
}
}
class Student {
private String name;
private int age;
public Person(String listStr) {
this.name = listStr.split(",")[0];
this.age = Integer.parseInt(listStr.split(",")[1]);
}
//省略带全参、无参构造,set、get方法
}
2.创建集合添加学生对象,学生对象属性:name,age。只获取姓名放到数组当中。
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
public class FunctionDemo2 {
public static void main(String[] args) {
ArrayList<Student> list = new ArrayList<>();
Student s1 = new Student("张三", 23);
Student s2 = new Student("李四", 24);
Student s3 = new Student("王五", 26);
Collections.addAll(list, s1, s2, s3);
String[] stringArr = list.stream()
.map(Student::getName)
.toArray(String[]::new);
System.out.println(Arrays.toString(stringArr)); //[张三, 李四, 王五]
}
}
class Student {
private String name;
private int age;
//省略带全参、无参构造,set、部分get方法
public String getName() {
return name;
}
}
3.创建集合添加学生对象,学生对象属性:name,age。将姓名年龄拼接成:”张三-23“的字符串,放到数组中
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
public class FunctionDemo3 {
public static void main(String[] args) {
ArrayList<Student> list = new ArrayList<>();
Student s1 = new Student("张三", 23);
Student s2 = new Student("李四", 24);
Student s3 = new Student("王五", 26);
Collections.addAll(list, s1, s2, s3);
String[] stringArr = list.stream()
.map(s -> s.getName() + "-" + s.getAge())
.toArray(String[]::new);
System.out.println(Arrays.toString(stringArr)); //[张三-23, 李四-24, 王五-26]
}
}
class Student {
private String name;
private int age;
//省略带全参、无参构造,set、get方法
}