让我们通过一个例子,资源处理(例如处理文件或数据库)时一个常见的模式就是打开一个资源,做一些处理,然后关闭资源。这个设置和清理阶段总是很类似,并且会围绕着执行处理的那些重要代码。这就是所谓的环绕执行(execute around)模式
例如,从一个文件中读取一行所需的模板代码
public static String processFile() throws IOException {
try (BufferedReader br = new BufferedReader(new FileReader("data.txt"))) {
return br.readLine();
}
}
如何利用Lambda和行为参数化来让代码更为灵活,更为简洁。
第一步:行为参数化
现在这段代码是有局限的。你只能读文件的第一行。如果你想要返回头两行,甚至是返回使用最频繁的词,该怎么办呢?在理想的情况下,你要重用执行设置和清理的代码,并告诉processFile方法对文件执行不同的操作。这是使用JDK8函数编程的优势就体现出来了。你只需要把processFile的行为参数化。你需要一种方法把行为传递给processFile,以便它可以利用BufferedReader执行不同的行为。传递行为正是Lambda的拿手好戏。那要是想一次读两行,这个新的processFile方法看起来又该是什么样的呢?基本上,你需要一个接收BufferedReader并返回String的Lambda
String result =processFile((BufferedReader br) ->br.readLine() + br.readLine());
第二步:使用函数式接口来传递行为
Lambda仅可用于上下文是函数式接口的情况。你需要创建一个能匹配BufferedReader -> String,还可以抛出IOException异常的接口。让我们把这一接口叫BufferedReaderProcessor吧。
@FunctionalInterface
public interface BufferedReaderProcessor {
String process(BufferedReader b) throws IOException;
}
第三步:执行一个传递进来的行为
任何BufferedReader -> String形式的Lambda都可以作为参数来传递,因为它们符合BufferedReaderProcessor接口中定义的process方法的定义。接下来你只需要在processFile主体内执行Lambda所代表的代码。请记住,Lambda表达式允许你直接内联,为函数式接口的抽象方法提供实现,并且将整个表达式作为函数式接口的一个实例。因此,你可以在processFile主体内,对得到的BufferedReaderProcessor对象调用process方法执行处理:
public static String processFile(BufferedReaderProcessor p) throws IOException {
try (BufferedReader br = new BufferedReader(new FileReader("data.txt"))) {
return p.process(br);
}
}
第四步:调用processFile方法时,传递自己行为Lambda表达式
现在就可以通过传递不同的Lambda重用processFile方法,并以不同的方式处理文件了。
获取处理文件一行:
String oneLine = processFile((BufferedReader br) -> br.readLine());
获取处理文件两行:
String twoLines = processFile((BufferedReader br) -> br.readLine() + br.readLine());
。
。
。
这样使我们的封装的processFile方法变得十分的灵活和简洁。