概述
Lambda表达式也可称为闭包,是Java 8的重要更新,也是一个被广大开发者期待已久的新特性。Lambda表达式支持将代码作为方法参数,Lambda表达式允许使用更简洁的代码来创建只有一个抽象方法的接口(这种接口被称为函数式接口)的实例。
Oracle 公司于 2014 年 3 月 18 日发布 Java 8,Lambda从2014年到2019年已经有5个年头的生命了,可以在实际的项目中的应用其实还是有限的,最主要的原因应该包括以下几个方面:
- Lambda仅仅是一种语法糖,用命令式编程的方式一样可以达到的效果,再学习的成本过高;
- 对于新手Javaer来说,刚刚理解到面向对象的精华后,以Lambda的写法简直是在挑战自己的理解力;
- Lambda的学习定义较抽象,内容较烦杂,应用较凌乱,再加上不清楚应用领域,致使放弃学习。
Lambda使用的目的:
- 为了简化(简洁)代码!
- 使用Java 8的Stream流式API处理集合。
因为Lambda是甜食,所以理解后,总是能尝到些甜头的,所以大家还是耐心的学习一下Lambda吧。
使用
1.Lambda语法定义
先来看看Lambda的直接使用的例子吧--匿名内部类的替换:
//匿名内部类方式创建线程,启动
public void oldRunable() {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("The old runable now is using!");
}
});
t.start();
}
//lambda方式创建线程,启动
public void runable() {
Thread t2 = new Thread(() -> System.out.println("It's a lambda function!"));
t2.start();
}
结果显而易见的lambda极大程度的简化了代码,代码的关键技术是创建一个Runnable接口的对象,重写接口里面的run方法,将这个对象传递到Thread的构造器里,让我们看看Lambda简化了什么:
- 编译器既然知道Thread的构造器里需要Runnable的对象,所以可以吧new Runnable省了;
- Runnable接口只有一个叫run的方法,所以方法名也省了;
lambda就是想尽一切方式简化代码,编译器能推理出来的东西交给机器去处理,程序员只要按照机器认识的方式编写必要出现的代码。
Lambda的语法定义,它由三部分组成
- 形参列表。可以省略形参的类型,因为编译器知道接口中方法类型。如果是只有一个参数那么连“()”也可以省;
- 剪头(->);
- 代码块。如果代码块只有一条语句可以将“{}”省略;如果只有一条return语句,那么“return”可以省略;
例子:来看看最全的写法(String a)-> { return a + "," ;}
简化后的写法: a -> a + "," (省了“()”,省了类型,省了return,省了花括号)
自己一定要多练习几个写法,这样再见到各种奇怪的Lambda写法就眼熟了。
至此你已经对Lambda的写法“了如指掌”,接下来要学习另一个新知识。
2.Lambda表达式与函数式接口
其实看了上面的内容对于Lambda的理解除了写法,也起不到别的作用,这里要介绍一个“目标类型(target type)”,
Lambda表达式的目标类型必须是“函数式接口(functional interface)”。函数式接口:只包含一个抽象方法的接口。接口可以包含多个默认方法、类方法,但关键是一个抽象方法。
Java8 的函数式接口有很多,如:Runnable,ActionListener,Filter,Comparator,新添加的Function,Predicate等,java8专门为函数式接口提供了@FunctionalInterface注解,可以使用此注解编写函数式接口,限制接口编写非函数式接口内容。
说白点就是Lambda表达式是要创建一个接口的对象,对象的类型是只有一个抽象方法的接口,来看看。
Runnable r = () -> System.out.println("我是java老手");
=号右面的是Lambda表达式,左边的是函数式接口,那么现阶段能使用Lambda的地方一下子就限制住了就给那“几个”接口来用。
哪里使用函数式接口,哪里就有Lambda
3.方法引用与构造器引用(::)
Lambda对比传统写法已经很简洁了,可以你以为上面的写法就是最简洁的了吗?不是。在上面Lambda的的语法定义中,如果Lambda表达式的代码块只有一条代码,还可以再代码块中使用方法引用和构造器引用。
Lambda的使用已经完成了,方法引用只有一点好处就是“简单代码”,不过缺点也明显不易理解和阅读不便,如果你完全不使用方法引用没问题,你所看的所有方法引用都可以换成原始Lambda表达式。
方法引用和构造器引用可以让Lambda表达式的代码在简洁的基础上再简洁,使用::双冒号来使用方法引用,看看有几种引用方式:
下面是一组例子,教你使用方法引用代替Lambda表达式:
//c1 与 c2 是一样的(静态方法引用)
Comparator<Integer> c2 = (x, y) -> Integer.compare(x, y);
Comparator<Integer> c1 = Integer::compare;
//下面两句是一样的(实例方法引用1)
persons.forEach(e -> System.out.println(e));
persons.forEach(System.out::println);
//下面两句是一样的(实例方法引用2)
persons.forEach(person -> person.eat());
persons.forEach(Person::eat);
//下面两句是一样的(构造器引用)
strList.stream().map(s -> new Integer(s));
strList.stream().map(Integer::new);
方法引用虽然精炼到了极致,可是使用时候阅读不易,所以请酌情。。。
4.Lambda表达式可使用的变量和作用域
@Test
public void test1(){
//将为列表中的字符串添加前缀字符串
String a= "lambda :";
List<String> proStrs = Arrays.asList(new String[]{"Ni","Hao","Lambda"});
List<String>execStrs = proStrs.stream().map(b-> {
Long c= System.currentTimeMillis();
return a + b+ " -----:" + c;
}).collect(Collectors.toList());
execStrs.forEach(System.out::println);
}
输出:
lambda :Ni -----:1498722594781 lambda :Hao -----:1498722594781 lambda :Lambda -----:1498722594781
变量a :外部变量
变量b:传递变量
变量c:内部自定义变量
lambda表达式可以访问给它传递的变量,访问自己内部定义的变量,同时也能访问它外部的变量。不过lambda表达式访问外部变量有一个非常重要的限制:变量不可变(只是引用不可变,而不是真正的不可变)。
Java中内部类以及Lambda表达式中也不允许修改外部类中的变量。
5.Lambda的应用
说了这么多Lambda表达式,目前使用最多的就是集合框架的应用,本章只是对Lambda语法的使用作介绍,以这章为基础的前提下再学习其应用。
将在下一章中给大家带来Lambda的主要使用场景--Stream流API的使用。
(完)