在项目中突然看到同事使用了一种看不懂的语法,以前多行的代码使用这种语法往往一行就能够达到同样的效果。一查资料才知道是Lambda表达式,Java在Version8中引入了该特性。不得不说自己有太久没有主动学习过新的东西,刚好项目忙过,能够抽空学习下Lambda表达式。
下面将由如下几个环节学习Lambda表达式:
- Lambda的定义
- Lambda的语法
- 如何在Java中使用
- Java中的内置函数型接口
Lambda定义
想要在Java中使用Lambda表达式,那么我们必须知道Lambda是个什么东西。简单来说,Lambda表达式就是一个匿名函数,也就是没有名称的函数。
了解了定义,那么如何书写一个Lambda表达式呢,这时他的语法就粉墨登场了。
Lambda语法
普通语法
(param1,param2,...) -> {
statement1,
statement2,
...
return statement3;
}
上面语法定义了Lambda表达式的通用语法,左边括号部分为参数,右边花括号部分为方法体,参数与方法体之间通过->
进行分隔。 如果细分又可以分为单参数和多参数:
- 单参数:可以省略左边括号,如:
param -> {
statement1,
statement2,
...
return statement3;
}
如果方法体只有一行时,甚至可以省略方法体的花括号,如:
param -> statement
- 多参数与通用语法一致。
方法引用、构造器引用语法
- 方法引用
// 实例名称::实例方法
objectName::instanceMethod
// 类名称::静态方法
ClassName::staticMethod
// 类名称::实例方法
ClassName::instanceMethod
方法引用调用者与方法之间使用::
进行分隔。
前面两种语法类似,等同于直接把Lambda表达式参数当成instanceMethod/staticMethod参数进行使用,例如:
System.out::println
等同于 s -> System.out.println(s)
;
Math::max
等同于(int x, int y) -> Math.max(x,y)
;
最后一种为类名称调用实例方法,等同于Lambda表达式把第一个参数作为方法调用者,后面参数作为方法参数进行使用,例如:
Object::equals
等同于(o1,o2) -> o1.equals(o2)
- 构造器引用
ClassName::new
等同于Lambda表达式将参数作为构造器的参数使用,例如: BigDecimal::new
等同于x -> new BigDecimal(x)
在Java中的使用
上面了解了Lambda表达式的语法,那么如何在Java中使用呢,下面我们将通过几个例子来进行说明。
- 在实际的项目中我们可能会对List进行一个排序,以前我们都是使用如下类似代码进行实现:
List<String> strs = Arrays.asList("1","2","3","4");
strs.sort(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
});
而使用Lambda表达式,则代码变成了如下:
strs.sort((s1,s2) -> s1.compareTo(s2));
或者
strs.sort(Comparator.comparing(String::toString));
以前需要5行的代码现在只需要1行,是不是简洁了许多?!
- 我们经常会对集合进行遍历,以前的写法是
List<String> strs = Arrays.asList("1","4","2","3");
for (String s : strs){
System.out.println(s);
}
而使用Lambda表达式,则代码变成了如下
strs.forEach(s -> System.out.println(s));
或者
strs.forEach(System.out::println);
Java中的内置函数型接口
从上面的两个例子可以看出Lambda表达式一般是使用在以匿名类作为参数的方法中,通过传入Lambda表达式对匿名类进行替换。需要注意的是,被替换的匿名类或接口只能有一个方法,这样的接口可以将之称之为函数式接口。同时可以在该接口上添加注解@FunctionalInterface
申明该接口为函数式接口。
在Java8中内置了大量的函数式接口供用户使用,下面为大家介绍几个常用的内置接口。
- Function<T, R>:函数型接口,接收类型为
T
的参数,并对其操作,然后返回类型为R
的结果。其中包含有apply(T t)
方法。
举例:对一个字符串进行截取并返回结果
// 申明方法
public String subStr(String str, Function<String, String> function){
return function.apply(str);
}
// 调用
subStr("我Code,我快乐", str -> str.substring(0, 2));
- Consumer<T>:消费型接口,接收类型为
T
的参数,对其进行操作。无返回值。其中包含有accept(T t)
方法。
举例:打印一句话
// 申明方法
public void print(String s, Consumer<String> consumer){
consumer.accept(s);
}
// 调用
print("我Code,我快乐", System.out::println);
- Supplier<T>:供应型接口,返回类型为
T
的结果。其中包含有T get()
的方法。
举例:获取一个UUID字符串
// 申明方法
static String get(Supplier<String> supplier){
return supplier.get();
}
// 调用
String uuid = get(() -> UUID.randomUUID().toString());
- Predicate<T>:断言型函数,接收类型为
T
的参数,对其进行断言,返回类型为boolean的结果。其中包含有boolean test(T t)
的方法。
举例:判断字符串是否以制定字符开头
// 申明方法
static boolean startWith(String str, Predicate<String> predicate){
return predicate.test(str);
}
// 调用
boolean result = startWith("I Like Code!", s -> s.startsWith("I"));
Java中内置的常用函数型接口为上述几种,相关接口源码皆可在包java.util.function
下进行查看,当然该包下还包含了其他众多函数型接口,有兴趣的可自行查看。
上述就是本篇文章的所有内容,边学习,边总结,最后再输出,这样才能更加有效的加深自己的理解和印象。希望自己在以后的学习中能够坚持。