Java 目前已经出到13的版本,但是国内大部分公司应该都停留在 Java 8 的版本(不敢承担升级带来的风险)。在Java8中给我们带来了 Lambda表达式和Stream流式操作,提供了函数式编程和简化批处理操作。可能大家日常使用很多,但是很少去关注Lambda 和 Stream实现。本篇就带着大家一起关注这两块知识,搬好小板凳我们一起看电视。
什么是Lambda
简单来说,编程中提到的 lambda 表达式,通常是指需要一个函数,但是又不想费神去命名一个函数的场合下使用,也就是指匿名函数。
一般我们使用变量来进行赋值操作:
但是在 Java8 之前是没有提供将一个方法赋值给一个变量的操作:
在 JavaScript 中有 闭包
的概念,所以 Java不甘落后,也去学习了人家,搞出了匿名函数的概念。
在Java 8里面,所有的Lambda的类型都是一个接口,而Lambda表达式本身,也就是”那段代码“,需要是这个接口的实现。这是我认为理解Lambda的一个关键所在,简而言之就是,Lambda表达式本身就是一个接口的实现。
基本语法:
(parameters) -> { expression or statements }
示例:
// 无参数, 返回1+2的结果
() -> 1+2;
// 接收一个参数(数字类型),返回其2倍的值
x -> 2 * x;
// 接收2个参数(数字),返回表达式运算的结果
(x, y) -> x + y;
// 多个语句要用大括号包裹, 并且返回值要用return指明
(x, y) -> {
int result = x + y;
System.out.print(result);
return result;
};
Lambda表达式有如下约定:
- 一个 Lambda 表达式可以有零个或多个参数;
- 参数的类型既可以明确声明,也可以根据上下文来推断。例如:
(int a)
与(a)
效果相同; - 所有参数需包含在圆括号内,参数之间用逗号相隔。例如:
(a, b)
或(int a, int b)
或(String a, int b, float c);
- 空圆括号代表参数集为空。例如:
() -> 42;
- 当只有一个参数,且其类型可推导时,圆括号()可省略。例如:
a -> return a*a;
- Lambda 表达式的主体可包含零条或多条语句;
- 如果 Lambda 表达式的主体只有一条语句,花括号{}可省略。匿名函数的返回类型与该主体表达式一致;
- 如果 Lambda 表达式的主体包含一条以上语句,则表达式必须包含在花括号{}中(形成代码块)。匿名函数的返回类型与代码块的返回类型一致,若没有返回则为空。
@FunctionalInterface 是 Java 8 新加入的一种接口,用于指明该接口类型声明是根据 Java 语言规范定义的函数式接口。Java 8 还声明了一些 Lambda 表达式可以使用的函数式接口,当你注释的接口不是有效的函数式接口时,可以使用 @FunctionalInterface 解决编译层面的错误。
根据定义,函数式接口只能有一个抽象方法,如果你尝试添加第二个抽象方法,将抛出编译时错误。例如:
@FunctionalInterface
public interface DoSomethingInterface {
void doSomeThing();
void doSomeThing1();
}
举一个简单使用的例子,首先定义一个接口:
@FunctionalInterface
public interface DoSomethingInterface<T> {
String doSomeThing(T t);
}
这个接口里面只能有一个函数,接口里是返回值为String类型的方法,那么下面使用它的时候就需要有返回值:
class MainTest{
public static void main(String[] args) {
DoSomethingInterface<String> t = str -> {
return str;
};
String print = t.doSomeThing("print");
System.out.println(print);
}
}
t就相当于是接口的具体实现,那么使用 t.doSomeThing("print")
就会去调用 t 的匿名函数执行。从这段代码看,如果我们有一个抽象类的具体实现逻辑相对简单不用大段代码的时候,可以使用 Lambda 表达式去实现具体的逻辑,这样就不用new 多个类。
Java 8 内置4大核心函数式接口
大家日常使用的List,Map,肯定大量使用了Stream流式处理,在流式处理中大量使用了Lambda,比如List的foreach方法就使用了Lambda表达式。Java8中抽象出来4类基本的函数式接口,日常开发中我们其实也是可以使用的。
函数式接口 | 参数类型 | 返回类型 | 用途 |
---|---|---|---|
Consumer |
T | Void | void accept(T t) :将T 类型的参数应用于该方法 |
Supplier |
T | T | T get():返回类型为T 的对象 |
Function<T,R> | T | R | R apply(T t):输出T类型的参数返回R类型的结果 |
Predicate |
T | Boolean | boolean test(T t): 确定类型为T 的参数是否满足test逻辑 |
以上4种类型是Java默认提供的Lambda类,开箱即用。
Consumer使用:
public void test(){
save(user, t->{ saveUser(t); });
}
public void save(User user, Consumer<User> consumer){
consumer.accept(user);
}
Supplier使用:
public void test(){
Config config = getConfig(t -> {
return getAllConfig(t);
});
}
public Config getConfig(Supplier<Config> supplier) {
return supplier.get();
}
Function使用:
public void test(){
User user = getUser(t -> { return getUserByName(t); });
}
public User getUser(Function<String, User> function) {
return function.apply(t);
}
Predicate使用:
public void test(){
boolean b = checkStatus(user, t -> {
return checkUserExist(t);
});
}
public boolean checkStatus(User user, Predicate<User> predicate) {
return predicate.test(user);
}
以上关于Lambda就介绍到这里,大家应该明白了它是干啥的了吧。闭包是不是也没有这么难懂呢!联想到List的foreach使用,大家有没有冲动想自己实现一个Lambda在日常开发中炫一把(如果别人看不懂,会打你的)。