Ссылка на метод - неверная ссылка метода - не может быть ссылкой из статического контекста

У меня есть следующий фрагмент кода

StringJoiner joiner = new StringJoiner(", ");
joiner.add("Something");
Function<StringJoiner,Integer> lengthFunc =  StringJoiner::length;
Function<CharSequence,StringJoiner> addFunc = StringJoiner::add;

Последняя строка вызывает ошибку

Error:(54, 53) java: invalid method reference
  non-static method add(java.lang.CharSequence) cannot be referenced from a static context

Я понимаю, что этот метод нельзя использовать статическим способом, и я должен иметь что-то вроде:

Function<CharSequence,StringJoiner> addFunc = joiner::add;

вместо этого. Однако я не могу понять, почему третья строка, с StringJoiner::length; для java-компилятора, абсолютно корректна. Может ли кто-нибудь объяснить мне, почему?

Ответы

Ответ 1

Потому что StringJoiner.length принимает нулевые аргументы, поэтому общая ссылка метода принимает StringJoiner (произвольный экземпляр) и возвращает Integer. Другими словами, первый назначенный метод ref эквивалентен:

Function<StringJoiner, Integer> lengthFunc = new Function<StringJoiner, Integer>() {

       @Override
        public Integer apply(StringJoiner stringJoiner) {
            return stringJoiner.length;
        }
}

Вы бы назвали это Function следующим образом:

StringJoiner sj1 = ...  // an arbitrary StringJoiner
int sjLength1 = lengthFunc.apply(sj1);

В контракте StringJoiner.add(CharSequence) принимает один аргумент, поэтому в целом Function должен был бы взять (1) экземпляр arbirary из StringJoiner, (2) CharSequence и вернуть a StringJoiner.

Вместо этого вы можете назначить ссылку на BiFunction, которая делает это:

BiFunction<StringJoiner, CharSequence, StringJoiner> addFunc = StringJoiner::add;

что эквивалентно:

BiFunction<StringJoiner, CharSequence, StringJoiner> addFunc = new BiFunction<StringJoiner, CharSequence, StringJoiner>() {

       @Override
        public StringJoiner apply(StringJoiner stringJoiner, CharSequence charSequence) {
            return stringJoiner.add(charSequence);
        }
}

и будет использоваться следующим образом:

StringJoiner sj1 = ...  // an arbitrary StringJoiner
sj1 = addFunc.apply(sj1, "a"); // no need to re-assign, but just to show the return type

Ответ 2

Function<StringJoiner,Integer> lengthFunc =  StringJoiner::length;

lengthFunc - это функция, которая принимает StringJoiner и возвращает Integer. Поэтому любой метод экземпляра StringJoiner, который ничего не принимает и возвращает Integer, соответствует этому интерфейсу. Экземпляр, вызванный этим методом, будет StringJoiner, требуемый Function<StringJoiner,Integer>.

С другой стороны, в

Function<CharSequence,StringJoiner> addFunc = StringJoiner::add

addFunc - это Function, который принимает CharSequence и возвращает StringJoiner. Никакой метод экземпляра StringJoiner не соответствует этому интерфейсу, так как функция не имеет входного экземпляра StringJoiner для применения метода add.

Вам понадобится BiFunction для соответствия сигнатуре StringJoiner::add:

BiFunction<StringJoiner,CharSequence,StringJoiner> addFunc = StringJoiner::add;