0 lambda的传说
Lambda 表达式”是一个匿名函数,可以包含表达式和语句,并且可用于创建委托或表达式目录树类型。
看百科的描述好像有那么点感觉了。可是干嘛非要搞个这么高冷的解释呢?
lambda 类似于一个可调用的代码块或者游离函数。当然也可以有入参。
1 瞄一眼他长啥样?
比如下面这样:
Comparator<Integer> c = (i, j) -> Integer.compare(i, j);
等价的原始Java代码长这样:
Comparator<Integer> c = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1, o2);
}
};
用上lambda是什么体验?
2 lambda各种外观
至少在Java里lambda的爸爸妈妈姐姐弟弟爷爷奶奶七大姑八大姨……可能都是长这个样子的。
同时,据老夫多年的断案经验来推断,lambda的本尊应该也是这个样子的:
(Type1 param1,Type2 param2, ...) -> {
return ret;
}
()->{return ret;}
(Type p)->{return ret;}
(p)->{return ret;}
p->{return ret;}
e->ret
e->{}
- 方法引用和构造器引用
- instance::instanceMethod
- className::staticMethod
- className::instanceMethod
Arrays.asList(null, "", " ", "HelloWorld", "ByeBug")
.stream().filter(StringUtils::isNotBlank)
.forEach(System.out::println);
3 lambda使用场景
再看一个例子:
new Thread(new Runnable() {
@Override
public void run() {
}
}).start();
new Thread(()->{}).start();
不难看出,整个匿名内部类中最关键的代码其实就是:
public void run() {
}
所以,lambda中关键部分也就是这部分代码了。
其实,用注解java.lang.FunctionalInterface
修饰的接口都可以用于lambda表达式中。
这种接口,都是只有一个方法的接口。
另外,只要你的接口只有一个方法,即使是没有@FunctionalInterface
注解修饰,也是可以用lambda的(很多时候编译器还是很聪明的,他会自动推断的)。
总之,lambda只钟爱函数式接口(@FunctionalInterface
)。
4 再来个示例
public class HouseInfo {
private Integer houseId;
private String houseName;
private Integer browseCount;
private Integer distance;
}
List<HouseInfo> houseInfos = Lists.newArrayList(
new HouseInfo(1, "恒大星级公寓", 100, 1),
new HouseInfo(2, "汇智湖畔", 999, 2),
new HouseInfo(3, "张江汤臣豪园", 100, 1),
new HouseInfo(4, "保利星苑", 23, 10),
new HouseInfo(5, "北顾小区", 66, 23),
new HouseInfo(6, "北杰公寓", null, 55),
new HouseInfo(7, "保利星苑", 77, 66),
new HouseInfo(8, "保利星苑", 111, 12)
);
Collections.sort(houseInfos, (h1, h2) -> {
if (h1 == null || h2 == null)
return 0;
if (h1.getDistance() == null || h2.getDistance() == null)
return 0;
return h1.getDistance().compareTo(h2.getDistance());
});
houseInfos.stream().map(h -> h.getHouseName()).forEach(System.out::println);
public static interface Condition<T> {
boolean conditionTest(T e);
}
public void printWithFilter(List<HouseInfo> houseList, Condition<HouseInfo> condition) {
houseList.stream().filter(e -> condition.conditionTest(e)).forEach(System.out::println);
}
@Test
public void test4() {
List<HouseInfo> houseInfos = Lists.newArrayList(
new HouseInfo(1, "恒大星级公寓", 100, 1),
new HouseInfo(2, "汇智湖畔", 999, 2),
new HouseInfo(3, "张江汤臣豪园", 100, 1),
new HouseInfo(4, "保利星苑", 23, 10),
new HouseInfo(5, "北顾小区", 66, 23),
new HouseInfo(6, "北杰公寓", null, 55),
new HouseInfo(7, "保利星苑", 77, 66),
new HouseInfo(8, "保利星苑", 111, 12)
);
printWithFilter(houseInfos, e -> e != null && e.getHouseName().contains("北"));
printWithFilter(houseInfos, e -> e != null && e.getDistance() != null && e.getDistance() > 10);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
Arrays.asList("HelloWord", "ByeBug")
.stream().map(String::toUpperCase).forEach(System.out::println);
List("HelloWord", "ByeBug").map(_.toUpperCase()).foreach(println _);
5 lambda的好基友
通过上面的示例,再结合实际。不难发现最常用的lambda的形式如下:
- 单个输入,无输出
- 单个输入,单个输出
- 无输入,输出单个类型
- 两个不同类型的输入,第三种类型的输出
- 两个不同类型的输入,其中一种类型的输出
- ……
其实在每次使用的时候,没必要自己去新建这些函数式接口以支持lambda,JDK就已经给lambda内置了很多好基友:
public interface Consumer<T> {
void accept(T t);
}
- BiConsumer: 两个不同类型的输入,无输出
public interface BiConsumer<T, U> {
void accept(T t, U u);
}
public interface Supplier<T> {
T get();
}
public interface Function<T, R> {
R apply(T t);
}
- ToIntFunction / ToLongFunction / ToDoubleFunction
public interface ToIntFunction<T> {
int applyAsInt(T value);
}
- IntFunction / LongFunction / DoubleFunction
public interface IntFunction<R> {
R apply(int value);
}
- BiFunction: 两个不同类型的输入,第三种类型的输出
public interface BiFunction<T, U, R> {
R apply(T t, U u);
}
public interface Predicate<T> {
boolean test(T t);
}
可以不用自定义函数式接口来支持lambda,上面的例子可以改成:
public void printWithFilter(List<HouseInfo> houseList, Predicate<HouseInfo> condition) {
houseList.stream().filter(e -> condition.test(e)).forEach(System.out::println);
}
关于lambda的传说和他到底是什么鬼,看到这里应该够了。
毕竟我们的主要目的是使用它,知道他怎么用才是重点。
没必要纠结他严格的学术定义(这种事不应该是那种只会纸上谈兵的老不死干的吗?)。
在Java8里和lambda相关的主要API就是Stream
了。在了解Stream的时候来顺便熟悉lambda吧。