java8-Lambda表达式的组成及使用

前面的文章中已经多次出现了Lambda表达式的身影,其简洁与灵活性相信读者也应有所感受。那么Lambda表达式究竟是个什么鬼?它的语法是什么?它由什么组成?我们该怎样使用Lambda表达式以及在哪里使用?本篇文章将解答这些问题。

Lambda表达式是什么?

可以把Lambda表达式理解为简洁地表示可传递的匿名函数的一种方式:它没有名称,但它有参数列表、函数主体、返回类型,可能还有一个可以抛出的异常列表。

  • 匿名——我们说匿名,是因为它不像普通的方法那样有一个明确的名称:写得少而想得多!
  • 函数——我们说它是函数,是因为Lambda函数不像方法那样属于某个特定的类。但和方法一样, Lambda有参数列表、函数主体、返回类型,还可能有可以抛出的异常列表。
  • 传递——Lambda表达式可以作为参数传递给方法或存储在变量中。
  • 简洁——无需像匿名类那样写很多模板代码。

Lambda表达式的语法与组成

Lambda表达式由参数、箭头、主体组成。如下图:

Lambda表达式的组成

  • 参数列表——这里它采用了Comparator中compare方法的参数,两个Apple。
  • 箭头——箭头->把参数列表与Lambda主体分隔开。
  • Lambda主体——比较两个Apple的重量。表达式就是Lambda的返回值了。

所以,Lambda表达式的基本语法可以总结为:
(parameters) -> expression 或 (parameters) -> { statements; }

对照上面的语法,下表列出了一些常用的Lambda表达式:

使用案例 Lambda示例
布尔表达式 (List list) -> list.isEmpty()
创建对象 () -> new Apple(10)
消费一个对象 (Apple a) -> {System.out.println(a.getWeight());}
从一个对象中选择/抽取 (String s) -> s.length()
组合两个值 (int a, int b) -> a * b
比较两个对象 (Apple a1, Apple a2) ->a1.getWeight().compareTo(a2.getWeight())

Lambda表达式的使用

在前面文章中那个过滤苹果的方法,我们用Lambda表达式实现的如下:

 
  1. List<Apple> greenApples = filter(inventory, (Apple a) -> "green".equals(a.getColor()));

那么为什么能这样使用呢?filter方法的第二个参数实际上是一个函数式接口Predicate<T>,它的定义如下:

 
  1. public interface Predicate<T>{
  2. boolean test (T t);
  3. }

函数式接口就是只定义一个抽象方法的接口

实际上,在java8以前,我们可能已经接触过下面这些函数式接口:

 
  1. //java.util.Comparator
  2. public interface Comparator<T> {
  3. int compare(T o1, T o2);
  4. }
  5. //java.lang.Runnable
  6. public interface Runnable{
  7. void run();
  8. }
  9. //java.awt.event.ActionListener
  10. public interface ActionListener extends EventListener{
  11. void actionPerformed(ActionEvent e);
  12. }
  13. //java.util.concurrent.Callable
  14. public interface Callable<V>{
  15. V call();
  16. }
  17. //java.security.PrivilegedAction
  18. public interface PrivilegedAction<V>{
  19. V run();
  20. }

Predicate<T>是java8中定义的一个新的函数式接口.

Lambda表达式允许我们以内联的方式实现一个函数式接口(Lambda表达式本身就是函数式接口的一个实例).

如下,我们即可以以函数式接口的方式,也可以以匿名类的方式传递runnable的实体,它们的实现效果相同,只是匿名类更为烦锁而已:

 
  1. //使用Lambda实例化runnable
  2. Runnable r1 = () -> System.out.println("Hello World 1");
  3. //使用匿名类,实例化runnable
  4. Runnable r2 = new Runnable(){
  5. public void run(){
  6. System.out.println("Hello World 2");
  7. }
  8. };
  9. public static void process(Runnable r){
  10. r.run();
  11. }
  12. process(r1);
  13. process(r2);
  14. //直接传递Lambda表达式
  15. process(() -> System.out.println("Hello World 3"));

从上面的代码中可以看出,Lambda表达式可以赋值给一个变量,也可以作为参数直接传递给以函数式接口为参数的方法,前提是Lambda表达式的签名要和函数式接口的抽象方法一样,这个抽象方法也叫做函数描述符,java编译器就是根据上下文来匹配函数描述符来判断Lambda表达式是否合法.

Lambda表达式在环绕执行模式(execute around)中的应用

通俗来讲,环绕执行模式(execute around)是指代码执行的首尾都是模版代码,真正重要的代码在中间部分执行,比如文件处理:先是打开文件,然后对文件进行处理,最后释放资源,这里我们关注的重点是对文件的处理,而不是打开文件与资源的释放.

而文件的处理,因为需求的不同会有所区别,所以Lambda表达式特别适合环绕执行模式的代码开发,像打开文件与释放资源这种一成不变的模版代码写在方法内,不同需求对应的不同行为以Lambda的形式传递进去。

代码如下:

 
  1. //定义函数式接口来传递处理文件的行为
  2. @FunctionalInterface
  3. public interface BufferedReaderProcessor {
  4. String process(BufferedReader b) throws IOException;
  5. }
  6. //定义处理文件的方法(环绕执行模式)
  7. public static String processFile(BufferedReaderProcessor p) throws IOException {
  8. try (BufferedReader br = new BufferedReader(new FileReader("data.txt"))) {
  9. return p.process(br);
  10. }
  11. }
  12.  
  13. //传递Lambda:读取一行
  14. String oneLine = processFile((BufferedReader br) -> br.readLine());
  15.  
  16. //传递Lambda:读取两行
  17. String twoLines = processFile((BufferedReader br) -> br.readLine() + br.readLine());

猜你喜欢

转载自blog.csdn.net/sdmxdzb/article/details/82740857