在方法引用的返回值上调用方法

时间:2016-04-22 16:09:56

标签: java java-8 java-stream

我有一个文件流,我想根据文件名的结尾进行过滤:

public Stream<File> getFiles(String ending) throws IOException {
    return Files.walk(this.path)
            .filter(Files::isRegularFile)
            .map(Path::toFile)
            .filter(file -> file.getName().endsWith(ending));
}

虽然最后一行中的lambda也不错,但我认为我也可以在那里使用方法引用,如下所示:

 .filter(File::getName.endsWith(ending));

或者用括号括起来。但是,这会因The target type of this expression must be a functional interface

而失败

你能解释为什么这不起作用吗?

3 个答案:

答案 0 :(得分:7)

  

你能解释为什么这不起作用吗?

方法引用是l​​ambda表达式的语法糖。例如,方法参考File::getName(File f) -> f.getName()相同。

Lambda表达式是用于定义功能接口实现的“方法文字”,例如FunctionPredicateSupplier等。

为使编译器知道您正在实现的接口,lambda或方法引用必须具有目标类型

// either assigned to a variable with =
Function<File, String> f = File::getName;
// or assigned to a method parameter by passing as an argument
// (the parameter to 'map' is a Function)
...stream().map(File::getName)...

或(异常)投射到某物:

((Function<File, String>) File::getName)

赋值上下文,方法调用上下文和强制转换上下文都可以为lambda或方法引用提供目标类型。 (在上述所有3种情况中,目标类型为Function<File, String>。)

编译器告诉你的是你的方法引用没有目标类型,所以它不知道如何处理它。

答案 1 :(得分:3)

File::getName是方法参考,String::endsWith也是。然而,它们不能被链接在一起。您可以创建另一种方法来执行此操作

public static Predicate<File> fileEndsWith(final String ending) {
    return file -> file.getName().endsWith(ending);
}

然后使用它

.filter(MyClass.fileEndsWith(ending))

如果你不重复使用它,这并不会给你带来太大的收益。

答案 2 :(得分:2)

一些帮助者可能会帮助提供一些你想要的外表。使用下面的帮助程序,您可以将lambda替换为包含方法引用的表达式,如下所示:

// since your predicate is on the result of a function call, use this to create a predicate on the result of a function
public static <A,B> Predicate<A> onResult(Function<A,B> extractor, Predicate<B> predicate){
    return a -> predicate.test(extractor.apply(a));
}

// since your predicate involves an added parameter, use this to reduce the BiPredicate to a Predicate with one less parameter
public static <T,U> Predicate<T> withParam(BiPredicate<T,U> pred, U param){
    return t -> pred.test(t,param);
}

public Stream<File> getFiles(String ending) throws IOException {
    return Files.walk(Paths.get("."))
            .filter(Files::isRegularFile)
            .map(Path::toFile)
            .filter(onResult(File::getName, withParam(String::endsWith, ending)));
}