Lambda初探

(a, b) -> a + b

上述是Lambda的基本语法,这是一种函数式编程的思想,是Java8引入的一个新概念,也叫做匿名函数。当然这并不是Java独有,比如Javascript在ES6规范中引入的箭头函数,也是lambda的运用。

何为匿名函数

说到匿名,首先想到的是java中的匿名类和匿名对象,比如我们简单的创建线程的方法:

class MyThread implements Runnable{
@Override
public void run() {
    System.out.println("hello lambda");
}
...
MyThread myThread = new MyThread();
Thread thread = new Thread(myThread);
thread.start();

通过自定义类实现Runnable,并实现其中的run方法,即可创建新的线程。但是如果上述线程只是使用一次,那么自定义类MyThread和对象mythread就显得有点鸡肋。

有的时候我们只是单纯的想新建一个线程来执行我们的方法,但是又不想像上面那样新建一个类并创建对象,那么使用以下方式:

new Thread(new Runnable(){
        @Override
        public void run() {
        System.out.println("hello lambda");
    }
}).start();

相较于之前的方式,上述代码明显简洁了很多,这里运用到的就是匿名对象

观察上述代码,我们只想要的是一个新的Thread来执行我们System.out.println("hello lambda")方法,而其他的比如方法名是啥,我们其实都不关心,其实可以使用以下方式:

new Thread(() -> System.out.println("hello lambda")).start();

相较于之前,代码变得更简洁和灵活,这里使用的就是匿名函数。匿名对象是没有名称的对象,同理,匿名函数,就是没有声明名称的函数方法,而Lambda表达式就是表示匿名函数的一种方式:没有名称,但是有参数列表和函数主体。

Lambda表达式

在前面我们可以看到,使用匿名类需要写一些笨重的代码,显得繁琐的冗长。通过Lambda表达式,可以更优雅的实现我们的目的。比如利用Lambda可以简洁的定一个Comparator对象。

现在有Apple这个类:

public Class Apple{
    private int weight;
    public int getWeight(){
        return weight;
    }
}

想要按照weight对Apple排序,可以使用匿名类的方式:

List<Apple> appleList = new ArrayList();
...
appleList.sort(new Comparator<Apple>() {
    public int compare(Apple a1, Apple, a2)(
        return a1.getWeight().compareTo(a2.getWeight());
    )
})

使用了Lambda之后:

appleList.sort((Apple a1, Apple, a2) -> a1.getWeight().compareTo(a2.getWeight());

很明显,代码看起来更清晰了。下面,来看一看上述代码中Lambda的基本结构:

(Apple a1, Apple, a2) -> a1.getWeight().compareTo(a2.getWeight()

Lambda表达式由三个部分组成:

  • 参数——使用Comparator中的compare方法的参数,传入两个苹果
  • 箭头——将参数和主体分隔开
  • Lambda主体——方法主体,比较两个苹果的重量,表达式也就是Lambda的返回值,返回类型为compare方法的返回值类型

可以看到,Lambda表达式语法很简单,不过,什么时候可以用Lambda呢。

上面的Runnable,Comparator有一个相似点:都是接口,而且都只定义了一个抽象方法,这类接口叫做函数式接口。虽然Java8之后接口可以有默认方法,但只要接口只定义了一个抽象方法,就仍然是一个函数式接口,而Lambda表达式可以理解为函数式接口的一个具体实现的实例。

方法引用

Lambda已经很好的简化了我们的代码,但是,能否更简化呢?

答案是可以的,比如上文中的

(Apple a1, Apple, a2) -> a1.getWeight().compareTo(a2.getWeight()

可以简化为:

comparing(Apple::getWeight)

这样代码可读性似乎更高,明确表达了我们想通过ApplegetWeight来比较大小,这种写法叫做方法引用

方法引用可以看作仅仅调用特定方法的Lambda的一种快捷写法。它的思想是:如果一个Lambda是直接调用某个现有的方法,那么最好是直接通过方法名去调用它,而不是描述如何去调用它。例如,上文中的Apple::getWeight,就是使用分隔符::来引用Apple类的getWeight方法,下面是一些Lambda的方法引用写法:

(Apple a) -> a.getWeight() 
Apple::getWeight

(String s) -> System.out.println(s)
System.out::println

(str, i) -> str.substring(i)
String::substring

方法引用可以看作是针对一些仅设计单一方法的Lambda的语法糖,让我们在某些时候能够用更少的代码更好的表达我们的想法。

参考:《Java 8 实战》