Java Lambda表达式
最近在学Java8的知识, 发现哪怕是自己会的Java基础还是远远不够的
匿名类和匿名函数
我们知道, Java是一门纯面向对象的语言, 对象是第一等公民, 意思是对象可以作为参数传递. 在很多语言中, 有一个概念, 叫做函数式编程, 函数也是一等公民, 也就是说函数也可以像对象啊一样作为参数传递, 这是怎么回事呢
首先, 让我们回顾一下匿名类. 在以前, 我们用Runnable定义线程先定义一个实现了Runnable接口的类, 然后传递到一个Thread对象中:
public class Test implements Runnable{
@Override
public boolean run() {
System.out.println("Hello world!");
}
}
public class Main{
public static void main(String[] args) {
Thread thread = new Thread(new Test());
}
}
之后我们可以用匿名类减少代码量:
public class Main{
public static void main(String[] args) {
Thread thread = new Thread(new Runnable(){
@Override
public boolean run() {
System.out.println("Hello world!");
}
});
}
}
但是, 匿名类的代码依然很长, 而且也不好理解(至少我当时就理解了很久…). 现在, 我们先试着使用匿名函数, 也就是lambda表达式:
public class Main{
public static void main(String[] args) {
Thread thread = new Thread(() -> System.out.println("Hello World!"));
}
}
这样代码就更加简洁了.
Lambda表达式
在上述例子中, 我们见到了() -> System.out.println("Hello World!")
, 这就是匿名函数, 也就是Lambda表达式, 我们使用Lambda表达式前必须先有函数式接口, 什么事函数式接口呢? 函数式接口就是只定义了一个抽象方法的接口. 上述例子中Runnable接口只有一个run()抽象方法, 所以它是一个函数式接口. 那么, Lambda表达式怎么书写呢?
在Lambda表达式中, ()里是参数, -> 后面是函数体, 如果函数有多行, 则需要将其用花括号包起来. 对于函数式接口, 我们都可以用Lambda表达式来代替, 但是只有函数描述符一致才行. 什么是函数描述符呢?
函数描述符
在有些语言中, 函数也是一个变量, 他们的类型是类似于()->void
的东西, 这一个表达式说明这是一个无参数无返回值的函数, 同理(int)->String
表示有一个变量为整型, 返回值为字符串的函数. 这个在java中就是函数描述符, 只要函数描述符和接口中的抽象方法一致时, Lambda表达式才是有效的.
对于只有一行的带返回值的Lambda表达式, 我们可以将return隐去, 下面两个式子是等价的:
(int a, int b) -> {return a + b}
(int a, int b) -> a + b
Lambda表达式应用
说了这么多, Lambda到底好用在哪里呢? 我们用下面一个例子来说明它的用法:
假设我们要从一堆苹果中进行筛选, 筛选出所有红色的苹果, 我们写了一下代码:
public List<Apple> filterApples(List<Apple> inventory) {
List<Apple> result = new ArrayList<>();
for (Apple apple : inventory) {
if ("red".equals(apple.getColor())) {
result.add(apple);
}
}
return result;
}
之后, 我们收到了新的要求: 筛选所有质量大于150的苹果, 于是我们书写了一下代码:
public List<Apple> filterApples(List<Apple> inventory) {
List<Apple> result = new ArrayList<>();
for (Apple apple : inventory) {
if (apple.getWeight() > 150) {
result.add(apple);
}
}
return result;
}
其实这个术后已经有冗余了. 之后又有了新要求: 同事筛选红色和重于150的苹果, 于是我们写下了下面一个接口:
public interface Predicate {
boolean test();
}
之后我们只要实现这个接口就好了:
public class AppleRedColorPredicate implements ApplePredicate {
@Override
public boolean test(Apple apple) {
return "red".equals(apple.getColor());
}
}
public class AppleHeavyPredicate implements ApplePredicate {
@Override
public boolean test(Apple apple) {
return apple.getWeight() > 150;
}
}
然后修改 filterApples() 方法:
public List<Apple> filterApples(List<Apple> inventory, ApplePredicate applePredicate) {
List<Apple> result = new ArrayList<>();
for (Apple apple : inventory) {
// 根据删选方法删选苹果
if (applePredicate.test(apple)) {
result.add(apple);
}
}
return result;
}
但是每次都写一个实现类实在是太麻烦了, 这个时候Lambda表达式的好处就体现出来了, 我们根据要求传递匿名函数进去就行了, 根本就不需要写实现类:
删选红苹果:
public void test(List<Apple> inventory) {
List<apple> result = filterApples(inventory, (Apple apple) -> "red".equals(apple.getColor());
}
删选重苹果:
public void test(List<Apple> inventory) {
List<apple> result = filterApples(inventory, (Apple apple) -> apple.getWeight() > 150;
}
删选又红又重的苹果:
public void test(List<Apple> inventory) {
List<apple> result = filterApples(inventory, (Apple apple) -> "red".equals(apple.getColor()) && apple.getWeight() > 150;
}
Lambda表达式的用处还有很多, 这就需要我不断学习不断摸索了.