1.先了解下什么是Lambda表达式
- Lambda表达式可以理解为可传递的匿名函数的一种方式:它没有名称,但它有参数列表、函数主体、返回类型,可能还有一个可以抛出的异常列表。
- Lambda表达式可以作为参数传递给方法或存储在变量中 Lambda表达式是简洁的
2.Lambda表达式的组成
lambda参数、->、lambda主体
示例:(int x,int y)-> {return x+y;}
3.Lambda基本语法
(parameters) -> expression 或
(parameters) -> { statements; }
4.Lambda如何使用
函数式接口:只定义一个抽象方法的接口
有一个注意的点:在java8中接口可以有默认的实现(即在类没有对方法进行实现时,其主体为方法提供默认实现的方法),除了默认的方法,抽象方法有且只有一个才能算函数式接口
Lambda表达式允许你直接内联,为函数式接口的抽象方法提供实现,并且将整个表达式作为函数式接口的一个实例。
函数描述符:函数式接口的抽象方法的签名(这里的签名指的是抽象方法的参数与返回类型)
行为参数化:以前我们写的方法都是将某个值或者Object作为参数在方法内部进行相对应得操作,行为参数化则是将操作值或者Object的行为作为参数传递
示例:
从一个文件中读取内容,通过行为参数化来进行不同的操作
1.定义一个readFile的Operation接口,这个接口就是一个函数式接口,它符合函数式接口的定义,有且只有一个抽象接口
@FunctionalInterface
public interface Operation {
public String readFile(BufferedReader bufferedReader) throws IOException;
}
2.在readOperation中使用operation接口的readFile方法,这一个方法的行为会在第3步给出来
public class FileOperation {
public String readOperation(Operation operation) throws IOException {
//此处是java 7中新加的语法糖,带资源的try语句不再需要我们显式的去关闭资源
try(BufferedReader bufferedReader = new BufferedReader(new FileReader("data.txt"))){
return operation.readFile(bufferedReader);
}
}
}
3.将不同的行为参数化,传递不同的Lambda表达式就可以有不同的方式处理文件内容
String line1 = readOperation((BufferedReader bufferedReader) -> bufferedReader.readLine());
String line2 = readOperation((BufferedReader bufferedReader) -> bufferedReader.readLine()+bufferedReader.readLine());
注明:
a.@FunctionalInterface
在java8中,新的java API通过@FunctionalInterface来标注函数式接口,这个标注用于表示该接口会设计成一个函数式接口,标注的接口有多个抽象接口(没有默认实现),会编译异常“Multiple non-overriding abstract methods found in interface xxxInterface”,@FunctionalInterface不是必需的
b.任何函数式接口都不允许抛出受检异常(checked exception)。如果你需要Lambda表达式来抛出异常,有两种办法:定义一个自己的函数式接口,并声明受检异常,或者把Lambda包在一个try/catch块中。
5.Lambda表达式中的类型检查、类型推断
Lambda表达式中的类型检查
Lambda的类型是从使用Lambda的上下文推断出来的。上下文(比如,接受它传递的方法的参数,或接受它的值的局部变量)中Lambda表达式需要的类型称为目标类型。
检查过程:
1.找出方法的声明
比如:public String readOperation(Operation operation) throws IOException
2.找到目标类型,比如Predicate<T -> Object>
T -> Object:T绑定到某一个对象,如:String、自定义对象 等
3.找到函数式接口中定义的抽象方法
4.抽象方法描述了一个函数描述符,它接收的参数以及返回的类型
5.方法的任何实际参数都必须匹配这个要求
标注:
只要不同抽象方法的参数与返回类型兼容,同一个Lambda表达式就可以与不同的函数式接口联系起来
Callable<Integer> c = () -> 42;
PrivilegedAction<Integer> p = () -> 42;
类型判断:
Java编译器可以从上下文(目标类型)推断出用什么函数式接口来配合Lambda表达式
进一步简化了Lambda表达式的写法
--没有类型推断,参数bufferedReader有显示参数
String line1 = readOperation((BufferedReader bufferedReader) -> bufferedReader.readLine());
--有类型推断,参数bufferedReader没有有显示参数
String line3= readOperation(bufferedReader -> bufferedReader.readLine());
--只有一个参数的时候,参数两边的括号也能省略
Operation operation = bufferedReader -> bufferedReader.readLine();
6.Lambda和局部变量
- Lambda允许使用方法主体外的变量,如同匿名内部类一样,这样的Lambda表达式称为捕获Lambda-
- Lambda使用这些变量是有限制的,只能是final修饰的变量
为什么会有这个限制
1.存储的位置不同,实例变量存储在堆上,局部变量存储在栈上
2.Lambda 表达在另一个线程中执行,也就是可能存在局部变量在被回收掉的情况下,去访问该局部变量;在用final修饰的情况下,去访问的只是这个变量的副本
--正常
int a = 10;
Runnable runnable = () -> System.out.println(a);
--编译报错
--Error:从lambda 表达式引用的本地变量必须是最终变量或实际上的最终变量
int a = 10;
Runnable runnable = () -> System.out.println(a);
a = 19;
总结:
- Lambda没有return语句,return语句是隐藏的 Lambda表达式可以包含多行语句 Lambda参数可以存在多个,也可以为0个
- Lambda表达式允许直接内联,为函数式接口的抽象方法提供实现,并且将整个表达式作为函数式接口的一个实例
- Lambda表达式的签名要与函数式接口中定义的抽象方法签名要一致 Lambda表达式会自动去推断类型
- Lambda表达式可以使用除主体外的变量,且主体外的变量必须用final修饰