10.Lambda表达式入门

使用Lambda表达式代替匿名内部类创建对象时,Lambda表达式的代码块会代替实现抽象方法的方法体,Lambda表达式就相当于一个匿名方法,由三部分组成:

-形参列表。形参列表允许省略形参类型。如果形参列表中只有一个参数,甚至连形参列表的圆括号都可以省略。

-箭头:"->",必须通过英文中横线符号和大于符号组成

-代码块:如果代码只包含一条语句,Lambda表达式允许省略代码块的花括号,那么这条语句就不要用花括号表示语句结束。Lambda代码块只有一条return语句,甚至可以忽略return关键字。Lambda表达式需要返回值,而它的代码块中如果仅有一条省略了return的语句,Lambda表达式会自动返回这条语句的值。

下面程序示范了Lambda表达式的几种简化写法:

 1 interface Eatable {
 2     void taste();
 3 }
 4 
 5 interface Flyable {
 6     void fly(String weather);
 7 }
 8 
 9 interface Addable {
10     int add(int a, int b);
11 }
12 
13 public class LambdaQs {
14     //调用该方法需要Eatable对象
15     public void eat(Eatable e) {
16         System.out.println(e);
17         e.taste();
18     }
19 
20     //调用该方法需要Flyable对象
21     public void drive(Flyable f) {
22         System.out.println("我正在驾驶:" + f);
23         f.fly("今天的风甚是喧嚣");
24     }
25 
26     //调用该方法需要Addable对象 
27     public void test(Addable add) {
28         System.out.println("5与3的和为:"+add.add(5,3));
29     }
30 
31     public static void main(String[] args) {
32         LambdaQs lq = new LambdaQs();
33         //Lambda表达式的代码块只有一条语句,可以省略花括号
34         lq.eat(() -> System.out.println("今天还没吃早餐,感觉有点饿"));
35         //Lambda表达式的形参列表只有一个形参,可以省略圆括号
36         lq.drive(weather -> {
37             System.out.println("今天的天气是:"+weather);
38             System.out.println("直升机飞行平稳");
39         });
40         //Lambda表达式的代码块只有一条语句时,可以省略花括号
41         //代码块中只有一条语句时,即使该表达式需要返回值,也可以省略return关键字
42         lq.test((a,b) -> a + b);
43     }
44 }

Lambda表达式与函数式接口

Lambda表达式的类型,也被称为"目标类型",Lambda表达式的目标类型必须是"函数式接口”,函数式接口代表只包含一个抽象方法的接口。函数式接口可以包含多个默认方法、类方法,但只能声明一个抽象方法。

如果采用匿名内部类语法来创建函数式接口的实例,则只需要实现一个抽象方法,在这种情况下即可采用Lambda表达式来创建对象,该表达式创建出来的对象的目标类型就是这个函数式接口。

由于Lambda表达式的结果就是被当成对象,因此程序中完全可以使用Lambda表达式进行赋值,例如如下代码:

 1 //Runnable接口中只包含一个无参数的方法
 2 //Lambda表达式代表的匿名方法实现了Runnable接口中唯一的,无参数的方法
 3 public class LambdaTest {
 4     public static void main(String[] args) {
 5         Runnable r = () -> {
 6             for(int i = 0 ; i < 200; i ++) {
 7                 System.out.println("\n"+i);
 8                 int n = i;
 9                 while(n-- > 0)
10                     System.out.print("-");
11             }
12         };
13 
14         r.run();
15     }
16 }

从上面代码中可以看出,Lambda表达式实现的是匿名方法——因此它只能实现特定函数式接口中的唯一方法,这意味着Lambda表达式有如下两个限制:

-Lambda表达式的目标类型必须是明确的函数式接口

-Lambda表达式只能为函数式接口创建对象。Lambda表达式只能实现一个方法,因此它只能为只有一个抽象方法的函数式接口创建对象。

关于上面第一点限制,看下面代码是否正确。

public class LambdaTest2 {
    public static void main(String[] args) {
        Object obj = () -> {
            for(int i = 0 ; i < 100  ; i ++ ) {
                System.out.println();
            }
        };
    }
}

上面代码与前一段代码机会完全相同,只是此时程序将Lambdas表达式不再赋值给Runnable变量,而是直接赋给Object变量,编译上面代码,会报如下错误:

从该错误信息可以看出,Lambda表达式的目标类型必须是明确的函数式接口。上面代码将Lambda表达式赋值给Object变量,编译器只能确定该Lambda表达式的类型为Object,而Object并不是函数式接口,因此上面代码报错。

为了保证Lambda表达式的目标类型是一个明确的函数式接口,可以有如下三种常见方式:

-将Lambda表达式赋值给一个明确的函数式接口类型的变量

-将Lambda表达式作为函数式接口类型的参数传给某个方法

-使用函数式接口对Lambda表达式进行强制类型转换。

因此,只要将上面代码改为如下形式即可:

Object obj = (Runnable)() -> {
            for(int i = 0 ; i < 100  ; i ++ ) {
                System.out.println();
            }
        };

 

方法引用和构造器引用

前面已经介绍过,如果Lambda表达式的代码块只有一条代码,程序就可以省略Lambda表达式中代码块的花括号,不仅如此,如果Lambda表达式的代码块只有一条代码,还可以在代码块中使用方法引用和构造器引用。

方法引用和构造器引用可以让Lambda表达式的代码块更加简洁。方法引用和构造器引用都需要使用两个英文冒号:Lambda表达式支持如下所示的几种引用方式。

1.引用类方法

先看第一种方法引用:引用类方法,例如,定义了如下函数式接口。

该函数式接口中包含一个convert()抽象方法,该方法负责将String参数转换为Integer。下面代码使用Lambda表达式来创建一个Convert对象,由于Lambda的代码块只有一条语句,因此程序省略了该代码块的花括号;而且由于表达式所实现的convert()方法需要返回值,因此Lambda表达式将会把这套代码的值作为返回值。

接下来程序就可以条用conveter对象的convert()方法将字符串转换成整数了。

interface Converter {
    Integer convert(String from);
}

public class LambdaTest3 {
    public static void main(String[] args) {
        //下面代码使用Lambda表达式创建Converter对象
        Converter converter = from -> Integer.valueOf(from);
        int num = converter.convert("976546541");
        System.out.println(num);
    }
}

2.引用某类对象的实例方法

例如,定义如下函数式接口。

 1 import java.util.*;
 2 interface MyTest {
 3     String test(String a, int b, int c);
 4 }
 5 
 6 public class LambdaTest4 {
 7     public static void main(String[] args) {
 8         MyTest mt = (a,b,c) -> a.substring(b,c);
 9         String answer = mt.test("I love coding, also love Java!",4,11);
10         System.out.println(answer);
11     }
12 }

 

Lambda表达式与匿名内部类的联系和区别

从前面可以看出,Lambda表达式是匿名内部类的一种简化,因此它可以部分取代匿名内部类的作用,Lambda表达式与匿名内部类存在如下相同点:

-Lambda表达式与匿名内部类一样,都可以直接访问"effectively final"的局部变量,以及外部类的成员变量。

-Lambda表达式创建的对象和匿名内部类生成的对象一样,都可以直接调用从接口中继承的默认方法。

 

区别:

-匿名内部类可以为任意接口创建实例,不管接口有多少个抽象方法,只要匿名内部类实现所有的抽象方法即可;但Lambda表达式只能为函数式接口创建实例。

-匿名内部类可以为抽象类甚至普通类创建实例;但Lambda只能为函数式接口创建实例。

-匿名内部类实现的抽象方法的方法体允许调用接口中定义的默认方法;但Lambda表达式的代码块不允许调用接口中定义的默认方法。

 

posted on 2018-08-13 11:11  nameless_vi  阅读(314)  评论(0编辑  收藏  举报

导航