Java8新特性lambda表达式快速入门
序
随着Java语言的不断发展,Java8提供的新特性lambda表达式也成为越来越多开发者喜欢的写法,为了顺应时代的变化,需要好好地学习这种写法,并应用在平时的开发中。我们先从简单的例子着手,一步步的深入到lambda的复杂写法,首先我们的任务是创建一个用来比较两个int值大小的接口,并给它添加具体的实现方法,然后进行调用。接口定义如下:
interface Comparator{
int compare(int a,int b);
}
常规写法
写法一:新建类并实现该接口
创建MyComparator.java并实现Comparator接口:
class MyComparator implements Comparator{
@Override
public int compare(int a, int b) {
return a > b ? a : b; //输出最大的值
}
}
然后进行调用:
public static void main(String[] args) {
Comparator comparator = new MyComparator();
int res = comparator.compare(1, 10);
System.out.println(res);
}
写法二:使用匿名函数
直接new接口
public static void main(String[] args) {
//使用匿名方法实现
Comparator comparator = new Comparator() {
@Override
public int compare(int a, int b) {
return a > b ? a : b;
}
};
int res = comparator.compare(1, 10);
System.out.println(res);
}
lambda写法
跟上面的常规写法不同的是,lambda只关注方法的入参和方法体中具体实现的逻辑,其余能省的代码全部都可以省略,我们先看一下第一种写法:
写法一:lambda常规写法
自己比较一下跟匿名函数写法的差别,是不是非常的简洁,lambda只声明了入参和方法体内的逻辑,关键地方在于入参和方法体之间使用了->
箭头符号进行连接,箭头符号读作gose to
。
public static void main(String[] args) {
//简单的lambda写法
Comparator comparator = (int a,int b) -> {
return a > b ? a : b;
};
int res = comparator.compare(1, 10);
System.out.println(res);
}
到这里你应该有一个基本的印象了,这一步转变非常重要,停下来好好思考一下。
写法二:lambda简写
仔细观察可以发现写法一中仍然有多余的代码,比如说我们在之前的接口中已经声明过一次入参的类型,在使用的时候又声明了一次,这显然是多余的写法。其实上面的写法依然可以更简洁,只要满足下面这几点,都可以省略。
- 当入参类型和初始定义类型一致时,可以省略入参类型。
- 当方法体中只有一句代码时,可以省略大括号。
- 当方法体中只有一句代码,并且需要返回值时,可以省略
return
关键字。
更简单的写法如下:
public static void main(String[] args) {
//省略了入参类型、大括号、return关键字
Comparator comparator = (a,b) -> a > b ? a : b;
int res = comparator.compare(1, 10);
System.out.println(res);
}
中场疑问
上面我们定义的接口只有一个抽象方法compare()
,有的同学就会有疑问,那如果有多个抽象方法怎么写呢?问的很好!由于lambda的写法非常的简洁,但这是在一定的条件限制下才能这么写,其中非常重要的一点就是,只有一个抽象方法的接口才支持lambda的写法,这种接口被称为函数式接口,可以通过@FunctionalInterface
注解进行修饰。
@FunctionalInterface只修饰函数式接口,即只有一个抽象方法的接口。
lambda的方法引用
写法一
lambda表达式中方法体只有一行代码才能有最简单的写法,那如果我想写一些复杂的业务就不能简写了吗?其实并不是的,这时候我们就可以泳道lambda的方法引用,将较复杂的业务封装成方法,然后在lambda中进行调用,假如还是上面的例子,我们可以将方法体中的那一行代码封装成一个方法:
public static int max(int a,int b){
//这里可以写一些较为复杂的业务
return a > b ? a : b;
}
这时候lambda表达式就可以这么写:
public static void main(String[] args) {
//方法体中引用max函数
Comparator comparator = (a,b) -> max(a,b);
int res = comparator.compare(1, 10);
System.out.println(res);
}
上面就是一个普通的lambda方法引用,简单的说就是将方法体中的代码封装成一个一方法,然后在调用这个方法即可,需要注意的是max
函数中的返回值必须要跟接口中的抽象方法一致,否则就会报异常。
写法二
上面的写法虽然简单有效,但依然有多余的地方,那就是方法参数重复了,代码(a,b) -> max(a,b);
中出现了两次(a,b)
,在实际开发中,我们一般采用另一种写法来代替->
箭头符号,请大家牢记语法:
方法的隶属者::方法名。
隶属者可以理解为,能够调用这个方法的对象,如果该方法是static
修饰的,则隶属者位当前类;如果不是static
修饰的,则隶属者属于一个对象。如果听不懂也没关系,看一个栗子就清晰明了。
首先看一下完整的代码:
//定义接口
interface Comparator {
int compare(int a, int b);
}
public class Program {
public static void main(String[] args) {
//使用lambda的方法引用调用max方法,隶属者为类
Comparator comparator = Program::max;
int res = comparator.compare(1, 10);
System.out.println(res);
}
//用static修饰的方法
public static int max(int a,int b){
return a > b ? a : b;
}
}
从上面代码中我们发现,将之前的(a,b) -> max(a,b);
写成了Program::max;
,因为方法max
被关键字static
修饰了,因此隶属者应该是Program
这个类。也可以发现省略了参数(a,b)
,这么写就已经是非常的简洁了,但这么写需要满足两个基本条件:
- 只有接口中抽象方法的参数格式跟定义的方法参数格式一样,才能省略参数。
- 接口中抽象方法的返回值必须跟定义的方法的返回值一样。
这就是java8引入的新特性,一切为了简洁明了。
方法不是static怎么写?
理解了上面,这个就比较好懂了,贴一下代码,不做解释了
public class Program {
public static void main(String[] args) {
//创建一个Program对象
Program p = new Program();
//使用lambda的方法引用调用max方法,隶属者为对象
Comparator comparator = p::max;
int res = comparator.compare(1, 10);
System.out.println(res);
}
//没有被static修饰的方法
public int max(int a,int b){
return a > b ? a : b;
}
}
练习题
素材如下:
Person.java
class Person{
String name ;
int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
准备集合
public class Program {
public static void main(String[] args) {
List<Person> list = new ArrayList<>();
list.add(new Person("张三",18));
list.add(new Person("李四",30));
list.add(new Person("王五",16));
list.add(new Person("赵六",20));
list.add(new Person("田七",40));
list.add(new Person("马八",25));
printList(list);
}
//打印集合
public static void printList(List<Person> list){
for (Person person : list) {
System.out.println(person);
}
}
}
1. 将集合按照指定属性进行排序
按照person的年龄进行从低到高排序
普通的排序方式:
//使用正常的方式排序
list.sort(new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o1.getAge() - o2.getAge();
}
});
问题:你能将普通的排序方式改造成lambda的方式吗?写出来后思考还有其他写法吗?
2. 将list.forEach
方法改造成lambda方式
正常的list遍历:
list.forEach(new Consumer<Person>() {
@Override
public void accept(Person person) {
System.out.println(person.toString());
}
});
问题:如何改造成lambda的方式??改造之后修改练习1中的遍历代码
3.删除集合中指定元素
删除年龄大于35的人
正常的删除:
list.removeIf(new Predicate<Person>() {
@Override
public boolean test(Person person) {
return person.age > 35;
}
});
问题:如何改造成lambda的方式??
答案
- 排序(二选一)
list.sort((p1,p2) -> p1.getAge() - p2.getAge());
Collections.sort(list,(p1,p2) -> p1.getAge() - p2.getAge());
- 遍历
list.forEach(person -> System.out.println(person.toString()));
- 删除指定元素
list.removeIf(person -> person.age > 35);