函数式接口就是只定义一个抽象方法的接口。在java8中,接口还可以拥有默认方法(即在类没有对方法进行实现时,其主体为方法提供默认实现的方法)。哪怕有很多默认方法,只要接口只定义了一个抽象方法,它就依然是一个函数式接口。
函数式接口带有@FunctionalInterface的标注,但它不是必须的。如果用@FunctionalInterface定义了一个函数式接口,而它却不是函数式接口的话,编译器会返回一个提示原因的错误。
Java API中已经有了几个函数式接口,比如Comparator,Runnable和Callable,java8在java.util.function包中引入了新的函数式接口
函数式接口 | 函数描述符 | 原始类型特化 |
Predicate<T> | T -> boolean | IntPredicate ... |
Consumer<T> | T -> void | IntConsumer ... |
Function<T,R> | T -> R | IntFunction ... |
Supplier<T> | () -> T | IntSupplier ... |
UnaryOperator<T> | T -> T | IntUnaryOperator ... |
BinaryOperation<T> | (T,T) -> T | IntBinaryOperation |
BiPredicate<L,R> | (L,R) -> boolean | |
BiConsumer<T,U> | (T,U) -> void | ObjIntConsumer ... |
BiFunction<T,U,R> | (T,U) -> R | ToIntBiFunction ... |
原始类型特化
java类型要么是引用类型(如Integer,List),要么是原始类型(如int,double)。但是泛型(比如Consumer<T>中的T)只能绑定到引用类型。在Java里有一个将原始类型转换为对应的引用类型的机制,叫做装箱;同样的,也有将引用类型转换为对应的原始类型,叫拆箱。装箱和拆箱是自动完成的,但是要付出性能的代价。装箱后的值本质上就是把原始类型包裹起来,并保存在堆里,因此装箱后的值需要更多的内存,并需要额外的内存搜索来获取被包裹的原始值。因此java8增加了原始类型特化,以便在输入和输出都是原始类型时避免自动装箱的操作
异常
任何函数式接口都不允许抛出受检异常。可以把Lambda表达式包在一个try/catch块中