Lambda表达式是java8的新特性,允许把函数作为一个方法的参数,使代码更加简洁。
语法:(argument) -> (body)
如:1
2
3(arg1, arg2...) -> { body }
(type1 arg1, type2 arg2...) -> { body }
不需要函数声明,以及函数名。(写法跟js中的Lambda基本是一样的)
下面是几个例子:1
2
3
4
5(int x, int y) -> x + y
() -> 42
(String s) -> { System.out.println(s); }
- 如果函数内的语句只有一句可以省略大括号与return,返回值就是语句的值
- 可以没有参数
- 只有一个参数时可以没有小括号
- 参数的类型可以不写,由编译器推导
下面看一下Lambda有什么用
代替匿名内部类
Runnable
1 | public class RunnableTest { |
Lamdba省略了new Runnable()接口名和run()方法名,全部交给编译器推导
之前我们在新建一个线程时会使用匿名内部类,现在使用Lamdba简洁又高效
Comparator
在下面的例子中,一个ArrayList包含Person类,对其进行排序:1
2
3
4
5
6
7
8
9public class Person {
private String givenName;
private String surName;
private int age;
private Gender gender;
private String eMail;
private String phone;
private String address;
}
1 | public class ComparatorTest { |
注:java.util.List也有sort的api,可以作为Collection.sort的替代品
Lambda的类型
Lambda的类型是什么?有些语言将Lambda看作对象,java为了保持旧版本向后兼容性,并没有这么做。
在java.util.function中,定义了多种函数式接口。以下是比较常用的:
示例代码:1
2Consumer a = (s) -> System.out.println("test" + s);
a.accept("test");
函数接口有 3 条重要法则:
- 一个函数接口只有一个抽象方法。
- 在 Object 类中属于公共方法的抽象方法不会被视为单一抽象方法。
- 函数接口可以有默认方法和静态方法。
任何满足单一抽象方法法则的接口,都会被自动视为函数接口。这包括 Runnable 和 Callable 等传统接口,以及我们自己构建的自定义接口。
自定义函数接口
要创建自己的函数接口,需要做两件事:
- 使用 @FunctionalInterface 注释该接口,这是 Java 8 对自定义函数接口的约定。
- 确保该接口只有一个抽象方法。
作为一个示例,我们将创建一个 Order 类,它有一系列 OrderItem 以及一个转换并输出它们的方法。我们首先创建一个接口。
下面的代码将创建一个 Transformer 函数接口。
@FunctionalInterface
public interface Transformer
T transform(T input);
}
该接口用 @FunctionalInterface 注释做了标记,表明它是一个函数接口。因为该注释包含在 java.lang 包中,所以没有必要导入。该接口有一个名为 transform 的方法,后者接受一个参数化为 T 类型的对象,并返回一个相同类型的转换后对象。转换的语义将由该接口的实现来决定。
这是 OrderItem 类:1
2
3
4
5
6
7
8
9
10
11
12
13
14public class OrderItem {
private final int id;
private final int price;
public OrderItem(int theId, int thePrice) {
id = theId;
price = thePrice;
}
public int getId() { return id; }
public int getPrice() { return price; }
public String toString() { return String.format("id: %d price: %d", id, price); }
}
OrderItem 是一个简单的类,它有两个属性:id 和 price,以及一个 toString 方法。
现在来看看 Order 类。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17import java.util.*;
import java.util.stream.Stream;
public class Order {
List<OrderItem> items;
public Order(List<OrderItem> orderItems) {
items = orderItems;
}
public void transformAndPrint(
Transformer<Stream<OrderItem>> transformOrderItems) {
transformOrderItems.transform(items.stream())
.forEach(System.out::println);
}
}
transformAndPrint 方法接受 Transform
这是一个使用该方法的样本:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21import java.util.*;
import static java.util.Comparator.comparing;
import java.util.stream.Stream;
import java.util.function.*;
class Sample {
public static void main(String[] args) {
Order order = new Order(Arrays.asList(
new OrderItem(1, 1225),
new OrderItem(2, 983),
new OrderItem(3, 1554)
));
order.transformAndPrint(new Transformer<Stream<OrderItem>>() {
public Stream<OrderItem> transform(Stream<OrderItem> orderItems) {
return orderItems.sorted(comparing(OrderItem::getPrice));
}
});
}
}
我们传递一个匿名内部类作为 transformAndPrint 方法的参数。在 transform 方法内,调用给定流的 sorted 方法,这会对订单项进行排序。这是我们的代码的输出,其中显示了按价格升序排列的订单项:
id: 2 price: 983
id: 1 price: 1225
id: 3 price: 1554
方法引用 ::
方法引用通过方法的名字来指向一个方法。它使用一对冒号 ::1
2
3
4
5
6
7
8
9
10
11
12//Old way:
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
for(Integer n: list) {
System.out.println(n);
}
//New way:
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
list.forEach(n -> System.out.println(n));
//or we can use :: double colon operator in Java 8
list.forEach(System.out::println);
使用双冒号来调用System.out类的静态方法println,或者调用某个对象的方法
streams
Java 8 增加了一些超棒的流 APIs。java.util.stream.Stream 接口包含许多有用的方法,能结合 Lambda 表达式产生神奇的效果。
我们将 Lambda 表达式 x -> x*x 传给 map() 方法,该方法会作用于流中的所有元素。之后,我们使用 forEach 方法打印数据中的所有元素:1
2
3
4
5
6
7
8
9
10//Old way:
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
for(Integer n : list) {
int x = n * n;
System.out.println(x);
}
//New way:
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
list.stream().map((x) -> x*x).forEach(System.out::println);
你可以访问下面的地址详细了解streams
Java 8 中的 Streams API 详解
参考链接
菜鸟教程-Java 8 Lambda 表达式
Java SE 8: Lambda Quick Start
深入浅出 Java 8 Lambda 表达式
为什么完美的 lambda 表达式只有一行
JDK8函数式接口Function、Consumer、Predicate、Supplier
Java 8 习惯用语,第 7 部分 函数接口
Java8:Lambda表达式增强版Comparator和排序