前言
最近在学黑马点评项目,发现里面用到了stream、方法引用等知识,因此对这些基础知识又进行了学习。
什么是方法引用
Java的方法引用可以理解为Lambda表达式的一种特殊形式,简化代码书写。
具体分类
- 静态方法引用
- 实例方法引用
- 构造方法引用
- 引用数组的构造方法
对于构造方法引用和数组的构造方法这里不多赘述,本文主要针对本人在学习当中对于实例方法是否需要new对象进行探讨,且主要针对如果使用而非具体原理。
静态方法引用
最典型的莫过于Integer了
Java
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "1", "2", "3");
list.stream().map(Integer::parseInt);
构造方法引用
类名::new
eg:Student::new
引用数组的构造方法
Java
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list, 1, 2, 3);
Integer[] arr = list.stream().toArray(Integer[]::new);
System.out.println(Arrays.toString(arr));
接下来重点讨论实例方法引用
实例方法引用
Java自带的类
对于Java自带的类来说,基本可以直接用类名,不需要new
Java
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "aaa", "bbb", "ccc");
list.stream().map(String::toUpperCase);
自定义类
对于自定义类来说,需要new
先定义一个学生类
Java
public class Student {
private String name;
private int age;
public boolean adult(Integer age){
return age >= 18;
}
}
再进行引用
Java
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list, 16, 20, 18);
list.stream().filter(new Student()::adult);
下面再引用黑马点评项目中的一段代码
ini
// 1.查询店铺信息
List<Shop> list = shopService.list();
// 2.根据店铺类型分组
Map<Long, List<Shop>> map = list.stream().collect(Collectors.groupingBy(Shop::getTypeId));
当时我就很纳闷为什么这个实例方法不需要用new呢?
后面发现这里List<Shop> list = shopService.list();
中的尖括号中的 Shop
表示这个 List
集合中元素的类型是 Shop
类型。也就是说,list
是一个包含 Shop
对象的集合。
当我们使用 list.stream().collect(Collectors.groupingBy(Shop::getTypeId))
这行代码时,我们已经有了一个由 Shop
对象组成的流,因为 list
集合中存储的就是 Shop
对象。
举个例子
还是刚刚那个Student类
Java
ArrayList<Student> list2 = new ArrayList<>();
Collections.addAll(list2,
new Student("张三",16),
new Student("李四",20),
new Student("王五",18)
);
list2.stream().filter(Student::adult).forEach(System.out::println);
可以理解为list2中已经有Student对象了,但是
,如果还是刚刚那个类,将会报错!
得加一个空参方法
Java
public boolean adult(){
return age >= 18;
}
为什么呢?我们来对比一下
Java
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list, 16, 20, 18);
list.stream().filter(new Student()::adult).forEach(System.out::println);
list.stream().filter(s->new Student().adult(s)).forEach(System.out::println);
list.stream().filter(new Predicate<Integer>() {
@Override
public boolean test(Integer integer) {
return false;
}
});
//-------------------------------------------------------------------
ArrayList<Student> list2 = new ArrayList<>();
Collections.addAll(list2,
new Student("张三",16),
new Student("李四",20),
new Student("王五",18)
);
list2.stream().filter(Student::adult).forEach(System.out::println);
list2.stream().filter(s->s.adult()).forEach(System.out::println);
list2.stream().filter(new Predicate<Student>() {
@Override
public boolean test(Student student) {
return false;
}
});
- 第一种获得的是Integer,需要有参的方法(有参数时)
在第一种情况中,使用了 list
这个 ArrayList
来存储整数类型的数据,然后通过 stream()
方法来创建一个流,并使用 filter
方法进行过滤。在 filter
方法中,使用了 new Student()::adult
这样的方法引用,假设 Student
类中有一个实例方法 adult
,那么它的方法签名应该是 public boolean adult(Integer age)
,这是因为方法引用 new Student()::adult
等价于使用 Lambda 表达式 (s) -> new Student().adult(s)
,因此需要一个参数 age
。
- 而第二种得到的是Student,需要无参方法(已经存在对象时)
而在第二种情况中,使用了 list2
这个 ArrayList
来存储 Student
对象,然后也是通过 stream()
方法创建流并使用 filter
方法进行过滤。在 filter
方法中,使用了 Student::adult
这样的方法引用,假设 Student
类中有一个实例方法 adult
,那么它的方法签名应该是 public boolean adult()
,因为在这种情况下,Student::adult
等价于使用 Lambda 表达式 (s) -> s.adult()
,不需要传入参数,因为 s
已经作为隐含参数传入了 adult
方法。
小结一下,所以对于自定义类的实例方法来说,
- 如果使用时用的是参数,则需要new,且要有对应的有参方法
- 如果使用时用的是已有对象,则不需要new,但是要有对应的无参方法
总结
当然,如果读者觉得这个方法引用过于复杂,可以写Lambda表达式,我们的Idea可以帮我们自动变成方法引用。
这些只是本人的拙见,可能存在错误,望大佬们指出。