业务开发中有个很常见的场景:
比如我现在有个List<Student> list 集合,我现在只想获取id集合或者我想做成Map<sId,sName>的格式,以便我在下面的逻辑里能够直接通过Map.get(sId)获取业务数据。
1. 代码里循环list转成map。
2.做成工具类,传入list,返回想要的key,value,方便。
但是好痛苦,,工具类要不断复制,或者有maven私服还好点,但是你到别的公司,是不是还得写新的?
Java8的lambda就能解决这个问题,一行搞定。今天来学习下。
函数式接口:
如果一个接口只有一个抽象方法,那么该接口就是函数式接口。
Java8里面接口不再只有抽象方法了,还有静态方法,默认方法。这里面的东西暂时不去管,先知道这么个概念。
接口静态方法:需要在接口里进行实现,通过接口名.方法名直接调用。
接口默认方法:需要在接口里进行实现,所有接口的实现类都默认继承该方法,子类可重写。
接口抽象方法:接口中没有实现的方法。
FunctionalInterface注解:
如果一个接口符合函数式接口的定义,那么就可以在接口上定义FunctionalInterface注解,用来表示这个接口是一个函数式接口,并且会按照函数式接口的规范在编译时进行检查。
如果一个接口只有一个抽象方法,但是没有这个注解,编译器依然会将该接口看做是函数式接口。
Lambda表达式:
那么我们知道了函数式接口,怎么理解Lambda表达式呢? 在Java里,一切皆是对象,在Java7的世界里,我们想要用一个接口的方法时,有两种方式:
1.定义一个实现类,重写方法。
2.定义一个匿名内部类,直接写在我们原有代码里,不需要新建一个实现类了。
那么Java8和这个类比的话,我们不需要写实现类,不需要写匿名内部类,我们用什么去做呢?就是Lambda表达式了。因此我的理解就是Lambda表达式 就是一个对象,它的对象类型就是 函数式接口。其实就是在说Lambda表达式就是一个接口的实现。那么只要一个对象时函数式接口的实现,就可以用Lambda表达式来写。
Lambda语法:
1.1基本语法
(Type1 param1, Type2 param2, ..., TypeN paramN) -> {
statment1;
statment2;
//.............
return statmentM;
}
1.2 单参数写法
param1 -> {
statment1;
statment2;
//.............
return statmentM;
}
List<String> nameList = list.stream().map(student -> {return student.getName();}).collect(Collectors.toList());
当左边的参数只有一个时,可以省略括号,比如有个Student list集合,我需要取一个List<String>name 的集合时,就可以写成上面的样子。
1.3单语句写法
param1 -> statment
List<String> nameList = list.stream().map(student -> student.getName()).collect(Collectors.toList());
单参数,单语句时写法最简洁。
1.4方法引用写法
上面是参数和语句的简洁写法,另外方法的调用上还能由其他简洁写法。
Class or instance :: method
可以看见,类名 + 方法名
List<String> nameList = list.stream().map(Student :: getName).collect(Collectors.toList());
1.5 可以外部变量
比如我要给每个Student 的名字加上后缀"同学"。
String suffix = "同学";
List<String> nameList = list.stream().map(student -> student.getName() + suffix).collect(Collectors.toList());
此处suffix被优化成了final变量,final变量特点是不可变(引用不可变,不是内容不可变)。
1.6 this的使用
public static void main(String[] args) {
Student s1 = new Student();
s1.setId(1);
s1.setName("测试1");
Student s2 = new Student();
s2.setId(2);
s2.setName("测试2");
List<Student> list = new ArrayList<>();
list.add(s1);
list.add(s2);
List<String> listName = new Main().getName(list);
System.out.println(listName);
}
private List<String> getName(List<Student> list){
return list.stream().map(student -> student.getName() + this.getSuffix()).collect(Collectors.toList());
}
private String getSuffix(){
return "同学";
}
this的用处依然没变。
2.1 方法引用和构造器引用
1.4 介绍了可以直接Class:: method,这就是Java8的语法糖。
方法引用:
类名 ::类方法
类名 ::实例方法
对象 ::实例方法
现有一个Student类:
public class Student {
private Integer id;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Student(Integer id, String name) {
this.id = id;
this.name = name;
}
public static int compareByNameStatic(Student s1, Student s2) {
return s1.getName().compareToIgnoreCase(s2.getName());
}
public int compareByNameInstance1(Student s1, Student s2) {
return s1.getName().compareToIgnoreCase(s2.getName());
}
public int compareByNameInstance(Student s2) {
return this.getName().compareToIgnoreCase(s2.getName());
}
}
类名 ::类方法
public class Test {
public static void main(String[] args) {
Student s1 = new Student(7,"测试7");
Student s2 = new Student(2,"测试2");
Student s3 = new Student(1,"测试1");
Student s4 = new Student(5,"测试5");
List<Student> list = new ArrayList<>();
list.add(s1);
list.add(s2);
list.add(s3);
list.add(s4);
//写法一
list.sort((temp1,temp2) -> Student.compareByNameStatic(temp1,temp2));
//优化写法二
list.sort(Student :: compareByNameStatic);
list.forEach(student -> System.out.println(student.getName()));
}
}
类名 ::实例方法
public class Test {
public static void main(String[] args) {
Student s1 = new Student(7,"测试7");
Student s2 = new Student(2,"测试2");
Student s3 = new Student(1,"测试1");
Student s4 = new Student(5,"测试5");
List<Student> list = new ArrayList<>();
list.add(s1);
list.add(s2);
list.add(s3);
list.add(s4);
//写法一
list.sort((st,temp2) -> st.compareByNameInstance(temp2));
//优化二
list.sort(Student :: compareByNameInstance);
list.forEach(student -> System.out.println(student.getName()));
}
}
对象 ::实例方法
public class Test {
public static void main(String[] args) {
Student s1 = new Student(7,"测试7");
Student s2 = new Student(2,"测试2");
Student s3 = new Student(1,"测试1");
Student s4 = new Student(5,"测试5");
List<Student> list = new ArrayList<>();
list.add(s1);
list.add(s2);
list.add(s3);
list.add(s4);
//写法一
list.sort((st,temp2) -> st.compareByNameInstance(temp2));
//优化二
list.sort(s1 :: compareByNameInstance1);
list.forEach(student -> System.out.println(student.getName()));
}
}
如果是调用实例方法时需要注意一个特征,抽象方法的第一个参数刚好是实例变量的类型。
构造器引用
暂时不去关注,没发现有啥用处。