16

I have following piece of code

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

Last line cause an error

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

I understand that this method can't be used in static way and I should have something like :

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

instead. However I can't understand why third line, with StringJoiner::length; is for java compiler perfectly correct. Can someboedy explain me why is that ?

Artur Skrzydło
  • 1,135
  • 18
  • 37

2 Answers2

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

lengthFunc is a Function that takes a StringJoiner and returns an Integer. Therefore any instance method of StringJoiner that takes nothing and returns an Integer matches this interface. The instance of which the method is called will be the StringJoiner required by the Function<StringJoiner,Integer>.

On the other hand in

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

addFunc is a Function that takes a CharSequence and returns StringJoiner. No instance method of StringJoiner matches this interface, since the function doesn't have an input StringJoiner instance to apply the add method on.

You would need a BiFunction to match the signature of StringJoiner::add :

BiFunction<StringJoiner,CharSequence,StringJoiner> addFunc = StringJoiner::add;
Eran
  • 387,369
  • 54
  • 702
  • 768
11

Because StringJoiner.length takes zero arguments, so the method reference overall takes a StringJoiner (the arbitrary instance) and returns an Integer. In other words, the first assigned method ref is equivalent to:

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

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

You would call this Function as follows:

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

In contract, StringJoiner.add(CharSequence) takes one argument, so overall the Function would have to take (1) the arbirary instance of StringJoiner, (2) the CharSequence and return a StringJoiner.

You can instead assign the reference to a BiFunction which does that:

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

which is equivalent to:

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

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

and would be used as follows:

StringJoiner sj1 = ...  // an arbitrary StringJoiner
sj1 = addFunc.apply(sj1, "a"); // no need to re-assign, but just to show the return type
M A
  • 71,713
  • 13
  • 134
  • 174
  • 1
    what does mean arbitraty instance of StringJoiner ? I see that it is treated as object type on which the apply method will be called, but I'm not sure how it works – Artur Skrzydło Jan 29 '17 at 13:23
  • 1
    @ArturSkrzydło I've elaborated with how they are equivalent using pure anonymous classes. By arbitrary instance, I mean some instance that would be used in the context of an invocation to the method reference. – M A Jan 29 '17 at 13:32
  • 1
    Thanks this is now clear for me. Great example how it would look like as implementation of anonymous functions. – Artur Skrzydło Jan 29 '17 at 13:38
  • one more question comes into my mind, what if i want to assign in the same way a function which takes two parameters as an argument ? Should I then write my own functional interface like `TripleFunction someFunc=StringJoiner:addWithSomeInteger ` ? – Artur Skrzydło Jan 29 '17 at 13:44
  • 2
    @ArturSkrzydło Yes you may have to write your own functional interface or use another workaround. You may want to check http://stackoverflow.com/questions/18400210/java-8-where-is-trifunction-and-kin-in-java-util-function-or-what-is-the-alt. – M A Jan 29 '17 at 13:54
  • 1
    To add that I personally would avoid a `TriFunction` (because it would make code more complicated) and make it more like a composite function where first I append the `Integer` to the `CharSequence`, and then call the original `BiFunction`. – M A Jan 29 '17 at 14:00