能够把Lambda表达式理解为简洁地表示可传递的匿名函数的一种方式:它没有名称,但它有参数列表、函数主体、返回类型,可能还有一个能够抛出的异常列表。html
(parameters) -> expression 或 (parameters) -> { statements; }
eg:(Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());
Lambda表达式有三个部分:java
函数式接口就是只定义一个抽象方法的接口。接口上标有@FunctionalInterface
表示该接口会设计成 一个函数式接口,若是你用@FunctionalInterface
定义了一个接口,而它却不是函数式接口的话,编译器将返回一个提示缘由的错误。接口如今还能够拥有默认方法(即在类没有对方法进行实现时, 其主体为方法提供默认实现的方法)。哪怕有不少默认方法,只要接口只定义了一个抽象方法,它就仍然是一个函数式接口。程序员
函数式接口的抽象方法的签名就是Lambda表达式的签名。咱们将这种抽象方法叫做:函数描述符。例如,Runnable接口能够看做一个什么也不接受什么也不返回(void)的函数的签名,由于它只有一个叫做run的抽象方法,这个方法什么也不接受,什么也不返回(void)。express
/**
* Represents a predicate (boolean-valued function) of one argument.
* <p>This is a <a href="package-summary.html">functional interface</a>
* whose functional method is {@link #test(Object)}.
* @param <T> the type of the input to the predicate
* @since 1.8
*/
@FunctionalInterface
public interface Predicate<T> {
/**
* Evaluates this predicate on the given argument.
* @param t the input argument
* @return {@code true} if the input argument matches the predicate,
* otherwise {@code false}
*/
boolean test(T t);
}复制代码
Predicate的英文示意是:谓词。Predicate接口定义了一个名叫test的抽象方法,它接受泛型T对象,并返回一个boolean。编程
/**
* Represents an operation that accepts a single input argument and returns no
* result. Unlike most other functional interfaces, {@code Consumer} is expected
* to operate via side-effects.
* <p>This is a <a href="package-summary.html">functional interface</a>
* whose functional method is {@link #accept(Object)}.
* @param <T> the type of the input to the operation
* @since 1.8
*/
@FunctionalInterface
public interface Consumer<T> {
/**
* Performs this operation on the given argument.
* @param t the input argument
*/
void accept(T t);
}复制代码
Consumer的英文示意是:消费者。Consumer接口定义了一个名叫accept的抽象方法,它接受泛型T对象,并无返回任何值。app
/**
* Represents a function that accepts one argument and produces a result.
* <p>This is a <a href="package-summary.html">functional interface</a>
* whose functional method is {@link #apply(Object)}.
* @param <T> the type of the input to the function
* @param <R> the type of the result of the function
* @since 1.8
*/
@FunctionalInterface
public interface Function<T, R> {
/**
* Applies this function to the given argument.
* @param t the function argument
* @return the function result
*/
R apply(T t);
}复制代码
Function的英文示意是:功能。Function接口定义了一个名叫apply的抽象方法,它接受泛型T对象,并返回一个泛型R的对象。ide
Java还有一个自动装箱机制来帮助程序员执行这一任务:装箱和拆箱操做是自动完成的。但这在性能方面是要付出代价的。装箱后的值本质上就是把原始类型包裹起来,并保存在堆里。所以,装箱后的值须要更多的内存,并须要额外的内存搜索来获取被包裹的原始值。Java 8为咱们前面所说的函数式接口带来了一个专门的版本,以便在输入和输出都是原始类型时避免自动装箱的操做。函数
Lambda的类型是从使用Lambda的上下文推断出来的。上下文(好比,接受它传递的方法的参数,或接受它的值的局部变量)中Lambda表达式须要的类型称为目标类型。类型检查过程能够分解为以下所示。性能
这段代码是有效的,由于咱们所传递的Lambda表达式也一样接受Apple为参数,并返回一个 boolean。请注意,若是Lambda表达式抛出一个异常,那么抽象方法所声明的throws语句也必 须与之匹配。有了目标类型的概念,同一个Lambda表达式就能够与不一样的函数式接口联系起来,只要它 们的抽象方法签名可以兼容。好比,前面提到的Callable和PrivilegedAction,这两个接口都表明着什么也不接受且返回一个泛型T的函数。 所以,下面两个赋值是有效的:this
Callable<Integer> c = () -> 42;
PrivilegedAction<Integer> p = () -> 42;复制代码
特殊的void兼容规则若是一个Lambda的主体是一个语句表达式, 它就和一个返回void的函数描述符兼容(固然须要参数列表也兼容)。例如,如下两行都是合法的,尽管List的add方法返回了一个 boolean,而不是Consumer上下文(T -> void)所要求的void:
// Predicate返回了一个boolean
Predicate<String> p = s -> list.add(s);
// Consumer返回了一个void
Consumer<String> b = s -> list.add(s);复制代码
Java编译器会从上下文(目标类型)推断出用什么函数式接 口来配合Lambda表达式,这意味着它也能够推断出适合Lambda的签名,由于函数描述符能够经过目标类型来获得。这样作的好处在于,编译器能够了解Lambda表达式的参数类型,这样就能够在Lambda语法中省去标注参数类型。
// 没有类 型推断
Comparator<Apple> c = (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());
// 有类型推断
Comparator<Apple> c = (a1, a2) -> a1.getWeight().compareTo(a2.getWeight()); 复制代码
Lambda表达式 也容许使用自由变量(不是参数,而是在外层做用域中定义的变量),就像匿名类同样。 它们被 称做捕获Lambda。Lambda捕获的局部变量必须显式声明为final, 或事实上是final。换句话说,Lambda表达式只能捕获指派给它们的局部变量一次。
方法引用能够被看做仅仅调用特定方法的Lambda的一种快捷 写法,方法引用看做针对仅仅涉及单一方法的Lambda的语法糖。目标引用放在分隔符::前,方法的名称放在后面。方法引用主要有三类:
对于一个现有构造函数,你能够利用它的名称和关键字new来建立它的一个引用: ClassName::new。它的功能与指向静态方法的引用相似。
Supplier<Apple> c1 = Apple::new;
Apple a1 = c1.get();
这就等价于:
Supplier<Apple> c1 = () -> new Apple(); // 利用默认构造函数建立 Apple的Lambda表达式
Apple a1 = c1.get(); // 调用Supplier的get方法 将产生一个新的Apple复制代码
Comparator<Apple> c = Comparator.comparing(Apple::getWeight);
// 逆序 按重量递 减排序
inventory.sort(comparing(Apple::getWeight).reversed());
// 比较器链 按重量递减排序;两个苹果同样重时,进一步按国家排序
inventory.sort(comparing(Apple::getWeight)
.reversed()
.thenComparing(Apple::getCountry));复制代码
// 产生现有Predicate 对象redApple的非
Predicate<Apple> notRedApple = redApple.negate();
// 连接两个谓词来生成另 一个Predicate对象 一个苹果既是红色又比较重
Predicate<Apple> redAndHeavyApple = redApple.and(a -> a.getWeight() > 150);
// 连接Predicate的方法来构造更复杂Predicate对象 表达要么是重(150克以上)的红苹果,要么是绿苹果
Predicate<Apple> redAndHeavyAppleOrGreen = redApple.and(a -> a.getWeight() > 150)
.or(a -> "green".equals(a.getColor()));
请注意,and和or方法是按照在表达式链中的位置,从左向右肯定优 先级的。所以,a.or(b).and(c)能够看做(a || b) && c。复制代码
andThen方法会返回一个函数,它先对输入应用一个给定函数,再对输出应用另外一个函数。 好比,
Function<Integer, Integer> f = x -> x + 1;
Function<Integer, Integer> g = x -> x * 2;
Function<Integer, Integer> h = f.andThen(g);
int result = h.apply(1);
数学上会写做g(f(x))或(g o f)(x)
这将返回4
compose方法,先把给定的函数用做compose的参数里面给的那个函 数,而后再把函数自己用于结果。
Function<Integer, Integer> f = x -> x + 1;
Function<Integer, Integer> g = x -> x * 2;
Function<Integer, Integer> h = f.compose(g);
int result = h.apply(1);
数学上会写做f(g(x))或(f o g)(x)
这将返回3复制代码
如下是你应从本章中学到的关键概念。