本文整理下Java8引入的最重要的函数式编程的特征 — lambda表达式,能让代码更加简单易懂。

Java中lambda表达式的关键特征:

在Lambda支持之前的代码块方式

在lambda支持之前,需要 显式定义类型 或者 使用匿名类 来支持代码块,下面展示一些例子。

线程任务

class Worker implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++)
           doWork();
    }
}

Worker w = new Worker();
new Thread(w).start();

数组排序

class LengthComparator implements Comparator<String> {
    @Override
    public int compare(String first, String second) {
        return Integer.compare(first.length(), second.length());
    }
}

Arrays.sort(strings, new LengthComparator());

匿名类

strings.sort(new Comparator<String>() {
    @Override
    public int compare(String first, String second) {
        return Integer.compare(first.length(), second.length());
    }
});

Lambda表达式语法

基本语法:(parameters) -> { statements; }

不需要指定返回类型,返回类型,能从上下文自动推断;同时,函数体如果有多个分支(如if),需要每个分支都有return值。

表达式单句形式

如果函数体只有一句,可以直接写该表达式,不用写return和大括号。

(String first, String second)
     -> Integer.compare(first.length(), second.length())

没有参数的形式

() -> { for (int i = 0; i < 1000; i++) doWork(); }

如果参数类型可以推断,可以不提供类型

Comparator<String> comp
     = (first, second) -> Integer.compare(first.length(), second.length());

如果只有一个参数,并且可以推断,则参数的小括号可以省略

Predicate<String> pIsEmpty = s -> s.isEmpty();

参数可以添加final控制符和注解

(final String name) -> ...
(@NonNull String name) -> ...

关于闭包特性(closure)

Lambda表达式可以引用表达式作用域外的变量,但是不允许对变量进行修改:

通过数组对象来改变捕获对象内部状态的例子:

Integer[] value = {0};
Runnable callback = () -> {
    value[0]++;
    System.out.println(value[0]);
};
callback.run(); // 输出 1

函数式接口(Functional Interfaces)

JDK1.8 之前已有的函数式接口

JDK1.8 新定义的函数式接口

java.util.function中定义了几组类型的函数式接口以及针对基本数据类型的子接口。

自定义函数式接口

函数接口定义好之后,就能用来保存lambda表达式。

public interface PrintVal<T> {
    void printVal(T t);
}

PrintVal<String> pv = s -> System.out.println(s);
pv.printVal("yeah");

方法引用

::操作符

可以通过::操作符引用现有的方法来传递给函数接口,支持3中方式:

关于this和super

方法引用还支持this和super的捕获

如:this::equals 等价于 x -> this.equals(x)

super::instanceMethod 可以引用超类中的方法:

class Greeter {
    public void greet() {
        System.out.println("Hello, world!");
    }
}

class ConcurrentGreeter extends Greeter {
    public void greet() {
        Thread t = new Thread(super::greet);
        t.start();
    }
}

构造函数引用

使用ClassName::new来引用构造函数

class StringHolder {
    String strVal;

    StringHolder(String strVal) {
        this.strVal = strVal;
    }
}

ArrayList<String> strs = new ArrayList<>();
strs.add("hello");
strs.add("world");

List<StringHolder> shList = strs.stream().map(StringHolder::new).collect(Collectors.toList());

另外,可以使用ClassName[]::new支持数组构造函数引用

关于接口 默认方法和静态方法

java8支持接口中定义default方法和static静态方法。

参考