为什么可以传递方法引用而不是转换器实例?

Why is it possible to pass method reference instead of converter instance?

我对 Java 8 中 method::lambdas 的解析类型有疑问。

鉴于此示例方法,它使用给定的 spring 核心转换器实现转换对象列表:

public static <S, T> List<T> convertToList(Collection<? extends S> input, Converter<S, T> converter) {
    List<T> results = new ArrayList<>(input != null ? input.size() : 0);
    if (input != null && !input.isEmpty()) {
        for (S element : input) {
            results.add(converter.convert(element));
        }
    }

    return results;
}

然后说我有一个特定的实现,BoatConverter:

public class BoatConverter implements Converter<Boat, BoatConverted> {

    @Override
    public BoatConverted convert(Boat boat) {
       // ...
    }

}

一种调用方式是:

ConversionHelper.convertToList(response.getBoats(), boatConverter)

但是,我注意到 Java 编译器(和预期结果)对此同样满意:

ConversionHelper.convertToList(response.getBoats(), boatConverter::convert)

在这种情况下给定 convert() returns 一个 BoatConverted,但是 convertToList() 需要一个 Converter 类型,我的问题是编译器如何解决这个明显的冲突,特别是因为你传入了一个 convert 的 lambda () 但 convertToList() 实用程序方法想要在传入的预期转换器对象上调用该方法?

Converter 实际上是一个功能接口,因为它只包含一个抽象方法:

package org.springframework.core.convert.converter;

public interface Converter<S, T> {
    T convert(S var1);
}

这意味着它可以用作 lambda 表达式的目标,您的示例可以重写为:

c -> boatConverter.convert(c)

在这种情况下,您的 lambda 只是将工作委托给 Converter 的现有实例,并且由于方法签名与接口方法匹配,因此可以用方法引用替换它。

主要区别在于,当您传递 boatConverter 时,您只是将此实例重新用作 Converter,但如果您编写 boatConverter::convertc -> boatConverter.convert(c) ,则您是创建一个新的 Converter 实例,将转换委托给 boatConverter

基本上,转换器实际上与 Function 相同(从一种类型转换为另一种类型),它是一个 @FunctionalInterface - 因为它只有一个抽象方法。

这就是为什么编译器可以很好地解决这个问题。考虑这个更简单的例子(IMO):

static class ToUpperCase implements Function<String, String> {

    @Override
    public String apply(String s) {
        return s.toUpperCase();
    }

}

private static List<String> transformList(List<String> input, Function<String, String> f) {
    return input.stream().map(f).collect(Collectors.toList());
}

这两个调用都有效:

transformList(list, func);
transformList(list, func::apply);

但是第二个可以写得更清楚:

transformList(list, String::toUpperCase);

这样你的例子可以更清楚:

ConversionHelper.convertToList(response.getBoats(), Boat::getYourField)

getYourField 显然应该替换为您正在做的任何事情。我发现这要冗长得多,因为它清楚地表明您正在 从 Boat 转换到它的某些领域