44

I'm struggling to see why the following code compiles:

public class MethodRefs {

    public static void main(String[] args) {
        Function<MethodRefs, String> f;

        f = MethodRefs::getValueStatic;

        f = MethodRefs::getValue;
    }

    public static String getValueStatic(MethodRefs smt) {
        return smt.getValue();
    }

    public String getValue() {
        return "4";
    }

}

I can see why the first assignment is valid - getValueStatic obviously matches the specified Function type (it accepts a MethodRefs object and returns a String), but the second one baffles me - the getValue method accepts no arguments, so why is it still valid to assign it to f?

codebox
  • 19,927
  • 9
  • 63
  • 81
  • 1
    Possible duplicate of [lambda with non-static methods in Java 8](http://stackoverflow.com/questions/25784337/lambda-with-non-static-methods-in-java-8) – Didier L Apr 25 '17 at 20:16
  • 1
    Also [:: (double colon) operator in Java 8](http://stackoverflow.com/questions/20001427/double-colon-operator-in-java-8/22245383#22245383) and [Use of double colons - difference between static and non-static method references](http://stackoverflow.com/questions/34982972/use-of-double-colons-difference-between-static-and-non-static-method-reference?noredirect=1&lq=1) – Didier L Apr 25 '17 at 20:20

5 Answers5

50

The second one

f = MethodRefs::getValue;

is the same as

f = (MethodRefs m) -> m.getValue();

For non-static methods there is always an implicit argument which is represented as this in the callee.

NOTE: The implementation is slightly different at the byte code level but it does the same thing.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • 24
    Note that the implicit argument can be made explicit (as of Java 8): `public String getValue(MethodRefs this)` is legal and identical to `public String getValue()`. – Pokechu22 Apr 20 '17 at 17:34
  • @Pokechu22 I didn't know that. Is there a name for that construct? – user253751 Apr 21 '17 at 01:09
  • 5
    @immibis: [the spec](http://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.4.1) (§8.4.1) calls it a "receiver parameter" as far as I can tell. – Pokechu22 Apr 21 '17 at 02:51
9

A non-static method essentially takes its this reference as a special kind of argument. Normally that argument is written in a special way (before the method name, instead of within the parentheses after it), but the concept is the same. The getValue method takes a MethodRefs object (its this) and returns a string, so it's compatible with the Function<MethodRefs, String> interface.

Wyzard
  • 33,849
  • 3
  • 67
  • 87
9

Lets flesh it out a bit:

import java.util.function.Function;

public class MethodRefs {

  public static void main(String[] args) {
    Function<MethodRefs, String> f;


    final MethodRefs ref = new MethodRefs();

    f = MethodRefs::getValueStatic;
    f.apply(ref);
    //is equivalent to 
    MethodRefs.getValueStatic(ref);

    f = MethodRefs::getValue;
    f.apply(ref);
    //is now equivalent to 
    ref.getValue();
  }

  public static String getValueStatic(MethodRefs smt) {
    return smt.getValue();
  }

  public String getValue() {
    return "4";
  }
}
Andreas
  • 4,937
  • 2
  • 25
  • 35
6

In the Java Tutorial it is explained that there are 4 different types of method references:

  1. reference to a static method
  2. reference to an instance method of a particular object
  3. reference to an instance method of an arbitrary object of a particular type
  4. reference to a constructor

Your case is #3, meaning that when you have an instance of MethodRef i.e. ref, calling apply on your function f will be equivalent to String s = ref.getValue().

fps
  • 33,623
  • 8
  • 55
  • 110
5

For non-static methods, the type of this is considered implicitly to be the first argument type. Since it's of type MethodRefs, the types check out.

Carcigenicate
  • 43,494
  • 9
  • 68
  • 117