[b]概述[/b]
Java 8添加了lambda表达式及类型推导的特性。这使得语言更加简练和整洁,然而正因这样,你没有明确说明自己究竟要做什么,这也带来了一些副作用。
[b]lambda表达式的返回值很重要[/b]
Java 8会去推导闭包的类型。推导的一个方式就是去查看它的返回值(或者是否有返回值)。而这带来的副作用令人咋舌。看下这段代码。
这段代码能够通过编译。然而,return null的这行,显得有些多余,你可能会想把它删掉。然而一旦你删掉了这行,它就会报错了。
这是使用FileReader的时候在报错。return null和捕获这个异常到底有什么关系?
[b]类型推导[/b]
ExecutorService.submit()是一个重载的方法。它有两个带单个参数的方法。
ExecutorService.submit(Runnable runnable);
ExecutorService.submit(Callable callable);
这些方法都不带参数,那么javac编译器如何能推导出lambda表达式的类型?它看的是返回值。如果你返回null,那就是Callable<Void>,然而如果不返回值,连null都没有了,那就是Runnble。
Callable和Runnable有一个重要的区别。Callable会抛出受检查异常,而Runnable则没有。
返回null的一个副作用就是你无需处理受检查异常,它们会在Future<Void> submit()中一起返回。如果你不返回任何东西的话,你就得自己去处理异常。
[b]结论[/b]
尽管lambda表达式和类型推导减少了许多模板代码,然而你会发现大量的边缘用例,那里编译器推导背后的隐藏细节会让人摸不着头脑。
[b]补充[/b]
你可以使用显式转换来进行类型推导。像这样
这个类型转换有一个副作用。call()方法不仅要返回一个Integer,它还添加了一个标记接口,lambda表达式生成的代码会因此改变,也就是说,它会增加一个writeObject()方法以及readObject()方法以支持lambda表达式的序列化。
注意:每个调用点都会生成一个新类,也就是说通过反射这个转换的细节是运行时可见的。
原创文章转载请注明出处:[url=http://it.deepinmind.com/java/2014/09/25/lambdas-and-side-effects.html]http://it.deepinmind.com[/url]
[url=http://vanillajava.blogspot.sg/2014/09/lambdas-and-side-effects.html]英文原文链接[/url]
Java 8添加了lambda表达式及类型推导的特性。这使得语言更加简练和整洁,然而正因这样,你没有明确说明自己究竟要做什么,这也带来了一些副作用。
[b]lambda表达式的返回值很重要[/b]
Java 8会去推导闭包的类型。推导的一个方式就是去查看它的返回值(或者是否有返回值)。而这带来的副作用令人咋舌。看下这段代码。
ExecutorService es = Executors.newSingleThreadExecutor();
es.submit(() -> {
try(Scanner scanner = new Scanner(new FileReader("file.txt"))) {
String line = scanner.nextLine();
process(line);
}
return null;
});
这段代码能够通过编译。然而,return null的这行,显得有些多余,你可能会想把它删掉。然而一旦你删掉了这行,它就会报错了。
Error:(12, 39) java: unreported exception java.io.FileNotFoundException; must be caught or declared to be thrown
这是使用FileReader的时候在报错。return null和捕获这个异常到底有什么关系?
[b]类型推导[/b]
ExecutorService.submit()是一个重载的方法。它有两个带单个参数的方法。
ExecutorService.submit(Runnable runnable);
ExecutorService.submit(Callable callable);
这些方法都不带参数,那么javac编译器如何能推导出lambda表达式的类型?它看的是返回值。如果你返回null,那就是Callable<Void>,然而如果不返回值,连null都没有了,那就是Runnble。
Callable和Runnable有一个重要的区别。Callable会抛出受检查异常,而Runnable则没有。
返回null的一个副作用就是你无需处理受检查异常,它们会在Future<Void> submit()中一起返回。如果你不返回任何东西的话,你就得自己去处理异常。
[b]结论[/b]
尽管lambda表达式和类型推导减少了许多模板代码,然而你会发现大量的边缘用例,那里编译器推导背后的隐藏细节会让人摸不着头脑。
[b]补充[/b]
你可以使用显式转换来进行类型推导。像这样
Callable<Integer> calls = (Callable<Integer> & Serializable) () -> { return null; }
if (calls instanceof Serializable) // is true
这个类型转换有一个副作用。call()方法不仅要返回一个Integer,它还添加了一个标记接口,lambda表达式生成的代码会因此改变,也就是说,它会增加一个writeObject()方法以及readObject()方法以支持lambda表达式的序列化。
注意:每个调用点都会生成一个新类,也就是说通过反射这个转换的细节是运行时可见的。
原创文章转载请注明出处:[url=http://it.deepinmind.com/java/2014/09/25/lambdas-and-side-effects.html]http://it.deepinmind.com[/url]
[url=http://vanillajava.blogspot.sg/2014/09/lambdas-and-side-effects.html]英文原文链接[/url]