题主问的是“lambda表达式”与“匿名内部类”相比,那当然是大大改善了可读性。
在Java语言层面还没有lambda表达式的时代,经过FP思想开化的程序员们早就开始用匿名内部类来做类似的事情了。但就是略别扭。
例如我以前在淘宝写Java代码经常会用google-collections / Guava,就看Guava官方文档的一段例子吧:
https:// code.google.com/p/guava -libraries/wiki/FunctionalExplained Multiset<Integer> lengths = HashMultiset.create( FluentIterable.from(strings) .filter(new Predicate<String>() { public boolean apply(String string) { return CharMatcher.JAVA_UPPER_CASE.matchesAllOf(string); } }) .transform(new Function<String, Integer>() { public Integer apply(String string) { return string.length(); } }));
这就是用匿名内部类来当FP风格的回调用。
如果用Java 8的话,这段代码可以直译为:
Multiset<Integer> lengths = HashMultiset.create( FluentIterable.from(strings) .filter(string -> CharMatcher.JAVA_UPPER_CASE.matchesAllOf(string)) .transform(string -> string.length()));
这好歹一眼就能看出来程序的意图是什么,而不会被匿名内部类的“类型一行、方法名一行,真正方法内容一行”的这种仪式给干扰到。
而完全融合进Java 8风格的API的话,上面代码会变成类似这样:
Multiset<Integer> lengths = strings.stream() .filter(string -> CharMatcher.JAVA_UPPER_CASE.matchesAllOf(string)) .map(string -> string.length()) .toHashMultiSet();
整个代码顺序就跟数据流动的顺序一致,意图也就更明确了。
至于这种适用lambda表达式 / 匿名内部类的风格的API好不好,那是另一个问题。
而lambda表达式会隐藏类型这点,不喜欢的话可以自己把类型全部写上去;lambda表达式的类型推导只是给了Java程序员一个选择,萝卜青菜反正自己挑。
前面
@戴威的回答里的代码例子:
File[] pics = folder.listFiles( f -> !f.isDirectory() && PICTURE_SUFFIX_LIST.stream().filter( s -> f.getName().toUpperCase().endsWith(s)).count() > 0);
其实重新格式化并适用更合适的API来写的话,可以是:
// File[] pics = folder.listFiles( file -> !file.isDirectory() && PICTURE_SUFFIX_LIST.stream() .anyMatch(suffix -> file.getName().toUpperCase().endsWith(suffix)) );
至少对我来说它的意图非常明确,比人肉循环filter好多了。
可读性主要取决于写代码的人,而非语言特性。
就拿上面的例子来说:
File[] pics = folder.listFiles( f -> !f.isDirectory() && PICTURE_SUFFIX_LIST.stream().filter( s -> f.getName().toUpperCase().endsWith(s)).count() > 0);
我对这里面出现的Java的库函数一窍不通,但是我基本上还是能看明白这段代码在干什么。
唯一要费脑子的地方还是这段代码的缩进导致的问题,一般来说,我会写成这样:
File[] pics = folder .listFiles( f -> !f.isDirectory() && PICTURE_SUFFIX_LIST .stream() .filter( s -> f.getName().toUpperCase().endsWith(s) ) .count() > 0 );
很明显这段代码的意图是非常清楚的
listFIles猜测为列出文件,f是文件,isDirectory是判断是否是目录,后面的stream.filter.count其实就是个Any。
所以意图就非常明确了,取出所有文件,这个文件不是目录,并且以PICTURE_SUFFIX_LIST里面某个字符串结尾。
RFX大大指出,Java也有anyMatch,所以这里可以进一步改写为:
File[] pics = folder .listFiles( f -> !f.isDirectory() && PICTURE_SUFFIX_LIST.stream() .anyMatch( s -> f.getName().toUpperCase().endsWith(s) ) );
事实上这段代码我在整理格式后已经用大脑直接转换成C#样式了:
File[] pics = folder .listFiles( f => f.IsDirectory == false && PICTURE_SUFFIX_LIST.Any( s => f.Name.EndsWith( s, StringComparision.IgnoreCaseOrdinal ) ) );
而通常这种逻辑我会写成:
var pics = from file in folder.EnumerateFiles() where PictureFileExtensions.Contains( file.Extension ) select file;
或者:
var pics = folder.EnumerateFiles() .Where( file => PictureFileExtensions.Contains( file.Extension );