首页 > 技术文章 > Lambda表达式

not2 2019-07-12 17:30 原文

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。拥有此注解的接口是函数式接口,只能定义一个方法,定义多个方法时会出现编译错误;即使没有此注解,只要接口只定义了一个方法,这个接口也是函数式接口。

 

推荐阅读