Lambda表达式,是Java8引入的一个重要新语法,它是一种紧凑的传递代码的方式。
Lambda表达式之前
首先,需要先回顾一下接口、匿名内部类和代码传递。以File类的listFiles(FilenameFilter filter)方法为例:
public File[] listFiles(FilenameFilter filter) { ... }
其中,FilenameFilter是一个接口,这种接口有个特点,就是只有一个方法:
@FunctionalInterface public interface FilenameFilter { boolean accept(File dir, String name); }
这个接口只有一个accept()方法,实际上仅从编程上说,FilenameFilter就是为了传递accept方法体代码的。
再看listFiles方法,它需要的其实不是一个FilenameFilter对象,而是它包含的accept(File dir, String name)方法。或者说,listFiles更希望直接接受一段方法代码作为参数,但是语法不支持方法作为参数,所以只能传递一个接口。
完整写法如下:
/*自定义一个FilenameFilter类*/ private class MyFilter implements FilenameFilter { @Override public boolean accept(File dir, String name) { return name.endsWith(".txt"); } } public void test() { MyFilter mFilter = new MyFilter(); //创建接口实例 File file = new File("/usr/bin"); file.listFiles(mFilter); //接口实例作为参数传入方法 }
另一种更简洁的方法是使用匿名内部类,这也是Lambda表达式之前最简洁的写法了:
public void test() { File file = new File("/usr/bin"); //使用匿名内部类 file.listFiles(new FilenameFilter(){ @Override public boolean accept(File dir, String name) { return name.endsWith(".txt"); } }); }
Lambda表达式
引入Lambda表达式之后,这种写法可以继续简化:
public void test() { File file = new File("/usr/bin"); //使用Lambda表达式 file.listFiles((File dir, String name) -> { return name.endsWith(".txt"); }); }
Lambda表达式由->分割成两部分,前面是方法的参数,后面{}内是方法的代码。因为这种接口只有一个方法,所以Java可以自行推断出参数类型,所以Lambda表达式的参数类型可以省略:
public void test() { File file = new File("/usr/bin"); //使用Lambda表达式,省略参数类型 file.listFiles((dir, name) -> { return name.endsWith(".txt"); }); }
上面的写法已经是Lambda表达式的一般写法了,但对于两种特殊情况,还有更加简洁的写法:
(1)当方法体代码只有一条语句的时候,括号{}和return语句也可以省略:
public void test() { File file = new File("/usr/bin"); //使用Lambda表达式,省略括号{}和return语句 file.listFiles((dir, name) -> name.endsWith(".txt")); }
(2)当方法只有一个参数的时候,括号()也可以省略(这里假设accept方法只有一个参数name):
public void test() { File file = new File("/usr/bin"); //使用Lambda表达式,省略括号() file.listFiles(name -> name.endsWith(".txt")); }
总结
对比代码可以看出,Lambda表达式要比匿名内部类在语法上简化很多。那Lambda表达式是不是仅仅是语法糖,其内部依然是匿名内部类呢?答案是否定的,Java会为每个匿名内部类生成一个类,但Lambda表达式不会。Lambda表达式的效率更高,它实际上是Java8引入的新概念:函数式接口。回看最上面的FilenameFilter接口定义,发现它有一个新的注解:@FunctionalInterface。拥有此注解的接口是函数式接口,只能定义一个方法,定义多个方法时会出现编译错误;即使没有此注解,只要接口只定义了一个方法,这个接口也是函数式接口。