方法引用不满足功能接口契约,但它编译。怎么可能?

Method reference does not fulfil the functional interface contract but it compile. How it is possible?

在下面的 class 中,我将方法引用 WordCounterEx::accumulate 作为第二个参数传递给 reduce 方法。 reduce方法的签名是:

<U> U reduce(U identity,
             BiFunction<U, ? super T, U> accumulator,
             BinaryOperator<U> combiner);

因此reduce方法的第二个参数,必须满足BiFunction配方。 但是传递的 accumulate 方法不是 BiFunction(它只有一个参数)。为什么它仍然编译?

public class WordCounterEx {
    private final int counter;
    private final boolean lastSpace;

    public WordCounterEx(int counter, boolean lastSpace) {
        this.counter = counter;
        this.lastSpace = lastSpace;
    }

    public int countWords(Stream<Character> stream) {
        WordCounterEx wordCounter = stream.reduce(new WordCounterEx(0, true),
                //HOW CAN THIS WORK? here must come BiFunction - R apply(T t, U u);
                WordCounterEx::accumulate,
                WordCounterEx::combine);
        return wordCounter.counter;
    }

    public WordCounterEx accumulate(Character c) {
        if(Character.isWhitespace(c)) {
            return lastSpace ?
                    this :
                    new WordCounterEx(counter, true);
        } else {
            return lastSpace ?
                    new WordCounterEx(counter+1, false) :
                    this;
        }
    }

    public WordCounterEx combine(WordCounterEx wordCounter) {
        return new WordCounterEx(counter + wordCounter.counter
                ,wordCounter.lastSpace /*does not matter*/);
    }
}

accumulate() 是实例方法,您通过 class 名称和方法名称(而不是实例和方法名称)来引用它。所以如果我想调用你给我的方法,我通常会做 myEx.accumulate(myCh)。因此,我提供了两件事,WordCounterEx 实例和角色。因此,以这种方式使用该方法算作 BiFunction<WordCounterEx, ? super Character, WordCounterEx>.

如果你给了我 this::accumulate,调用方法的对象就会被给定 (this),它不能再用作 BiFunction(在我的 Eclipse 中,我得到“Stream 类型中的方法 reduce(U, BiFunction, BinaryOperator) 不适用于参数 (WordCounterEx, this::accumulate, WordCounterEx::combine)”)。

WordCounterEx#countWords方法可以重写如下:

public int countWordsWithInstance(Stream<Character> stream) {
    WordCounterEx wordCounter = stream.reduce(new WordCounterEx(0, true),
            this::accumulate,
            WordCounterEx::combine);
    return wordCounter.counter;
}

public WordCounterEx accumulate(WordCounterEx wc,Character c) {
    if(Character.isWhitespace(c)) {
        return wc.lastSpace ?
                wc :
                new WordCounterEx(wc.counter, true);
    } else {
        return wc.lastSpace ?
                new WordCounterEx(wc.counter+1, false) :
                wc;
    }
}

在这种情况下,accumulate 方法必须在其签名中包含 WordCounterEx wc