Java lambda表达式

Java lambda表达式,用户希望在代码中使用 lambda 表达式,使用某种 lambda 表达式语法,并将结果赋给函数式接口类型的引用。函数式接口是一种包含单一抽象方法(single abstract method)的接口。

Java lambda表达式 问题描述

用户希望在代码中使用 lambda 表达式。

Java lambda表达式 解决方案

使用某种 lambda 表达式语法,并将结果赋给函数式接口类型的引用。

Java lambda表达式 具体实例

函数式接口是一种包含单一抽象方法(single abstract method)的接口。类通过为接口中的所有方法提供实现来实现任何接口,这可以通过顶级类(top-level class)、内部类甚至匿名内部类完成。
Runnable 接口为例,它从 Java 1.0 开始就已存在。该接口包含的单一抽象方法是 run,它不传入任何参数并返回 voidThread 类构造函数传入 Runnable 作为参数,例 1-1 显示了 Runnable 接口的匿名内部类实现。

例 1-1 Runnable 接口的匿名内部类实现

public class RunnableDemo {
    public static void main(String[] args) {
        new Thread(new Runnable() {  ➊
            @Override
            public void run() {
                System.out.println(
                    "inside runnable using an anonymous inner class");
            }
        }).start();
    }
}

➊ 匿名内部类
匿名内部类语法以关键字 new 开头,后面跟着 Runnable 接口名以及英文小括号,表示定义一个实现该接口但没有显式名(explicit name)的类。大括号({})中的代码重写 run 方法,将字符串打印到控制台。
例 1-2 中的代码采用 lambda 表达式,对例 1-1 进行了改写。

例 1-2 在 Thread 构造函数中使用 lambda 表达式

new Thread(() -> System.out.println(
    "inside Thread constructor using lambda")).start();

上述代码使用箭头将参数与函数体隔开(由于没有参数,这里只使用一对空括号)。可以看到,函数体只包含一行代码,所以不需要大括号。这种语法被称为 lambda 表达式。注意,任何表达式求值都会自动返回。在本例中,由于 println 方法返回的是 void,所以该表达式同样会返回 void,与 run 方法的返回类型相匹配。
lambda 表达式必须匹配接口中单一抽象方法签名的参数类型和返回类型,这被称为与方法签名兼容。因此,lambda 表达式属于接口方法的实现,可以将其赋给该接口类型的引用。
例 1-3 显示了赋给某个变量的 lambda 表达式。

例 1-3 将 lambda 表达式赋给变量

Runnable r = () -> System.out.println(
    "lambda expression implementing the run method");
new Thread(r).start();

Java 库中不存在名为 Lambda 的类,lambda 表达式只能被赋给函数式接口引用。

“将 lambda 表达式赋给函数式接口”与“lambda 表达式属于函数式接口中单一抽象方法的实现”表示相同的含义。我们可以将 lambda 表达式视为实现接口的匿名内部类的主体。这就是 lambda 表达式必须与抽象方法兼容的原因,其参数类型和返回类型必须匹配该方法的签名。注意,所实现方法的名称并不重要,它不会作为 lambda 表达式语法的一部分出现在代码中。
因为 run 方法不传入参数,并且返回 void,所以本例特别简单。函数式接口 java.io.FilenameFilter 从 Java 1.0 开始就是 Java 标准库的一部分,该接口的实例被用作 File.list 方法的参数,只有满足该方法的文件才会被返回。
根据 Javadoc 的描述,FilenameFilter 接口包含单一抽象方法 accept,它的签名如下:

boolean accept(File dir, String name)

其中,File 参数用于指定文件所在的目录,String 用于指定文件名。
例 1-4 采用匿名内部类来实现 FilenameFilter 接口,只返回 Java 源文件。

例 1-4 FilenameFilter 的匿名内部类实现

File directory = new File("./src/main/java");

String[] names = directory.list(new FilenameFilter() {  ➊
    @Override
    public boolean accept(File dir, String name) {
        return name.endsWith(".java");
    }
});
System.out.println(Arrays.asList(names));

➊ 匿名内部类
在例 1-4 中,如果文件名以 .java 结尾,accept 方法将返回 true,否则返回 false
而例 1-5 采用 lambda 表达式实现 FilenameFilter 接口。

例 1-5 FilenameFilter 接口的 lambda 表达式实现

File directory = new File("./src/main/java");

String[] names = directory.list((dir, name) -> name.endsWith(".java"));  ➊
    System.out.println(Arrays.asList(names));
}

➊ lambda 表达式
可以看到,代码要简单得多。参数包含在小括号中,但并未声明类型。在编译时,编译器发现 list 方法传入一个 FilenameFilter 类型的参数,从而获知其单一抽象方法 accept 的签名,进而了解 accept 的参数为 FileString,因此兼容的 lambda 表达式参数必须匹配这些类型。由于 accept 方法的返回类型是布尔值,所以箭头右侧的表达式也必须返回布尔值。
如例 1-6 所示,我们也可以在代码中指定数据类型。

例 1-6 具有显式数据类型的 lambda 表达式

File directory = new File("./src/main/java");

String[] names = directory.list((File dir, String name) ->  ➊
    name.endsWith(".java"));

➊ 显式数据类型
此外,如果 lambda 表达式的实现多于一行,则需要使用大括号和显式返回语句,如例 1-7 所示。

例 1-7 lambda 代码块

File directory = new File("./src/main/java");

String[] names = directory.list((File dir, String name) -> {  ➊
    return name.endsWith(".java");
});
System.out.println(Arrays.asList(names));

➊ 代码块语法
这就是 lambda 代码块(block lambda)。在本例中,虽然代码主体只有一行,但可以使用大括号将多个语句括起来。注意,不能省略 return 关键字。
lambda 表达式在任何情况下都不能脱离上下文存在,上下文指定了将表达式赋给哪个函数式接口。lambda 表达式既可以是方法的参数,也可以是方法的返回类型,还可以被赋给引用。无论哪种情况,赋值类型必须为函数式接口。

赞(7)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

Java 实例