引文
在这篇文章中,我想介绍一下Java 8最吸引人的新特性——lambda表达式。
先贴上lambda表达式的基本语法,有两种形式:
- (parameters) -> expression
- (parameters) -> {expressions}
lambda允许把函数作为一个方法的参数传递进方法中,体现了函数式编程思想。使用lambda表达式可以使代码变得更加简洁紧凑。lambda表达式的本质只是一个所谓的“语法糖”,并没有为Java语言添加新的功能,而是对已有功能进行了封装,由编译器推断并转换为常规的代码,因此可以用更少的代码来实现同样的功能。我个人觉得,lambda表达式虽好,但不要乱用,用在合适的地方,可以有效减少代码量,使代码更加简洁,而滥用只会降低代码的可读性,使程序难以调试和维护。那么lambda表达式的应用场景是什么呢?
网上有很多lambda表达式的例子,我看了半天,觉得无外乎就两个场景(个人浅显观点......):
一是代替函数式接口。函数式接口简单来说就是只包含一个抽象方法的接口,比如Java标准库中的java.lang.Runnable和java.util.Comparator都是典型的函数式接口。对于函数式接口,除了可以使用Java中标准的方法来创建实现对象之外,还可以使用lambda表达式来创建实现对象,这可以在很大程度上简化代码的实现。在使用lambda表达式时,只需要提供形式参数和方法体。由于函数式接口只有一个抽象方法,所以通过lambda表达式声明的方法体肯定是这个唯一的抽象方法的实现,而且形式参数的类型可以根据方法的类型声明进行自动推断(即形式参数可以省略类型)。
第二个场景,就是和集合配合使用。Java 8新增了两个对集合数据进行批量操作的包:java.util.function和java.util.stream。可以说,lambda表达式和stream是自Java语言添加泛型和注解以来最大的变化,lambda表达式很大程度上影响了我们在处理集合时的编码方式。
Lambda表达式基本用法
下面,我通过几个典型的例子带大家领略lambda表达式的强悍:
使用lambda表达式实现Runnable接口
传统方式
public class runnableTest {
public static void main(String[] args) {
new Thread(new Runnable() {
public void run() {
System.out.println("I am Hawk.");
}
}).start();
}
}
复制代码
使用lambda表达式
public class runnableTest {
public static void main(String[] args) {
new Thread(() ->
System.out.println("I am Hawk.")
).start();
}
}
复制代码
通过这个例子,我们也可以进一步熟悉lambda表达式的声明方式:由形式参数和方法体两部分组成,中间通过“->”分隔。形式参数通常情况下不需要包含类型声明,可以进行自动推断。因为 System.out.println("I am Hawk.") 是单行语句,可以不用花括号“{}”括起来,如果方法体包含多行语句,则需要用“{}”括起来。
使用lambda表达式实现Comparator接口
关于Comparator的机制,不在我们这次的讨论范围内,我有时间...额...大概会写。总之,当我们相比较的对象没有实现Comparable接口时,可以使用Comparator接口并实现其中的compare方法进行比较。比如说,有这样一个Penple类:
// 使用lombok注解自动生成构造器与get、set方法等
@Data
@NoArgsConstructor
@AllArgsConstructor
public class People {
private String name;
private Integer age;
}
复制代码
现在我想按照年龄对People对象进行排序:
传统方式
public class ComparatorTest {
public static void main(String[] args) {
List<People> peopleList = new ArrayList<>();
peopleList.addAll(Arrays.asList(new People("a", 20), new People("b", 21), new People("c", 22)));
Collections.sort(peopleList, new Comparator<People>() {
@Override
public int compare(People p1, People p2) {
return p1.getAge().compareTo(p2.getAge());
}
});
}
}
复制代码
使用lambda表达式
// 方法一
public class ComparatorTest {
public static void main(String[] args) {
List<People> peopleList = new ArrayList<>();
peopleList.addAll(Arrays.asList(new People("a", 20), new People("b", 21), new People("c", 22)));
Collections.sort(peopleList, (p1, p2) ->
p1.getAge().compareTo(p2.getAge()));
}
}
// 方法二
public class ComparatorTest {
public static void main(String[] args) {
List<People> peopleList = new ArrayList<>();
peopleList.addAll(Arrays.asList(new People("a", 20), new People("b", 21), new People("c", 22)));
peopleList.sort(Comparator.comparing(People::getAge));
}
}
复制代码
不知道大家咋想的,反正我当时知道还有方法二这种写法时是真的惊了。感觉Java 8中的集合类型在增加了lambda表达式的支持之后变得......emmm高深莫测。需要注意一点,在方法二中,还使用了Java 8的另一个新特性——方法引用,就是那两个冒号“::”(有点C++的赶脚)。方法引用可以在不调用某个方法的情况下引用一个方法,是另一种实现函数式接口的方法,可以进一步简化代码。
使用lambda表达式对列表进行迭代
传统方法
public class foreachTest {
public static void main(String[] args) {
List<String> stringList = new ArrayList<>();
stringList.addAll(Arrays.asList("a", "b", "c"));
for (String str : stringList) {
System.out.println(str);
}
}
}
复制代码
使用lambda表达式
public class foreachTest {
public static void main(String[] args) {
List<String> stringList = new ArrayList<>();
stringList.addAll(Arrays.asList("a", "b", "c"));
stringList.forEach(System.out::println);
}
}
复制代码
额......先写到这里吧,因为stream部分内容较多,过几天再补充完。这次主要介绍了使用lambda表达式的第一个场景——代替函数式接口。这是一个趋势,希望大家在今后的编码中,能逐渐抛弃匿名内部类,改用lambda这种更为简洁的写法。下次我将重点介绍lambda在集合中的使用以及如何与stream进行配合。