Java8中的新特性
一、Lambda表达式
Lambda表达式可以理解为一种可传递的匿名函数:它没有名称,但又参数列表、函数主体、返回类型,可能还有一个可以抛出的异常列表。
匿名:和匿名类类似的,它没有明确的名字
函数:Lambda函数不属于某个特定的类,但和方法一样都具有参数列表、函数主体、返回类型,还可以有抛出的异常列表
传递:Lambda表达式可以作为参数传递给方法或者存储在变量中。
Lambda表达式的基本形式: (parameters)-> expression 或(parameters)-> {statements;}
怎么使用Lambda表达式?
哪里可以使用Lambda表达式?你可以在函数式接口上使用Lambda表达式。
函数式接口也就是只定义了一个抽象方法的接口。Lambda表达式允许直接以内联的形式为函数式接口的抽象方法提供实现,并把整个表达式作为函数式接口的实例。
上面既然提到了Lambda表达式是函数式接口的抽象方法的具体实现,那这两者之间的函数描述符应该是一致的,才可以编译成功。
如
public void process(Runnable r){ r.run(); } process(()->System.out.println(“this is awesome”));
Runnable接口的函数描述符为()->void,而其对应的Lambda表达式实现也为()->void。签名一致的情况下才能通过类型检查。
函数式接口:
在Java8以前,常用的函数式接口Comparable、Runnable、Callable等。而在Java8中又引入了一系列的函数式接口。
常用的函数式接口
函数式接口
函数描述符
原始类型特化
Predicate<T>
T->boolean
由于Java中存在将原始类型自动转化为对应包装类型的机制以及对应的逆过程(装箱和拆箱),但进行这一操作会影响性能(装箱后的值是将基本类型包裹起来,并保存在堆中,这样一来就需要更多的内存,并需要额外的内存搜索来获取被包裹的值。故而Java8为函数式接口提供了原始类型特化的版本,以便在输入和输出都是原始类型时避免自动装箱,针对转梦的输入参数类型的函数式接口的名称都要加上对应的基本类型前缀。如DoublePredicate,LongPredicate,IntPredicate)
Consumer<T>
T->void
Function<T>
T->R
Supplier<T>
()->T
UnaryOperator<T>
T->T
BinaryOperator<T>
(T,T)->T
BiPredicate<L,R>
(L,R)->boolean
BiConsumer<T,U>
(T,U)->void
BiFunction<T,U,R>
(T,U)->R
类型检查:
Lambda的类型是从使用Lambda的上下文(比如接受它传递的方法的参数或者接受它的值的局部变量)中推断出来的,而我们且将上下文要求Lambda表达式需要的类型称为目标类型。当目标类型的抽象方法的函数描述符和Lambda表达式的函数描述符一致时,即类型检查无误。这样我们就可以将一个Lambda表达式应用到不同的函数式接口中,只要二者的函数描述符一致即可。
注意点:特出的void兼容规则
如果一个Lambda的主体是一个语句表达式,它就和一个返回void的函数描述符兼容(参数列表兼容的前提下)
类型推断:Java编译器可以从上下文推断出用什么函数式接口来配合Lambda表达式,也能推断出使用Lambda的前面,这样就可以在Lambbda语法中省去标注参数类型。
Lambda与闭包:
闭包:闭包是一个函数的实例,可以无限制地范围那个函数的非本地变量。如闭包可以作为参数传递给另一个函数,也可以访问和修改器作用域之外的变量。
而Java8中Lambda表达式和匿名类可以做类似于闭包的事情,可以作为参数传递给方法,并且可以访问其作用域之外的变量,但存在一个限制,它们不能修改定义Lambda的方法的局部变量的内容,这些变量都必须是隐式最终的。而匿名类则为显式最终的(可以理解为是对值封闭,而不是对变量封闭)
二、方法引用: