8

Why does the following not work?

import java.util.function.Function;

public class MethodRefTest {
   public String passMeAround( String input ) {
      return input + " been passed to me";
   }

   public Function<String, String> testReferences() {
      final Function<String, String> f1 = MethodRefTest::passMeAround;
      return f1;
   }

   public static void main( String[] args ) {
      new MethodRefTest()
            .testReferences()
            .apply( "foo" );
   }
}

Javac tells me:

MethodRefTest.java:14: error: invalid method reference
      final Function<String, String> f1 = MethodRefTest::passMeAround;
                                          ^
  non-static method passMeAround(String) cannot be referenced from a static context
1 error

I don't understand why the context is static. I read this but it does not seem to answer the problem at hand.

EDIT

Also according to oracle, "Reference to an instance method of an arbitrary object of a particular type" are possible via ContainingType::methodName

EDIT 2

@marko-topolnik helped me understand my mistake here. Function<String, String> f1 = MethodRefTest::passMeAround; refers to a static method String MethodRefTest.passMeAround(String)

BiFunction<MethodRefTest, String, String> f1 = MethodRefTest::passMeAround;

on the other hand, refers to the instance method of any instance, that is passed in the apply clause.

BiFunction<MethodRefTest, String, String> f2 = MethodRefTest::passMeAround;
//call instance method
f2.apply(new MethodRefTest(), "some string");
Community
  • 1
  • 1
Benjamin
  • 1,726
  • 1
  • 14
  • 35
  • 3
    @AlexandroSifuentesDíaz time to learn Java 8. It's out for quite some time now. – JB Nizet Aug 27 '15 at 16:18
  • 1
    But @BrianGoetz, `class::method` can (and _does_ in OP's case) resolve to an instance method reference. The error message is misleading because the real problem is the mismatch between the target type and the type of the instance method reference. – Marko Topolnik Aug 28 '15 at 07:06
  • 1
    @dabai Regarding your Edit 2, it's a very good explanation for why the compiler may have come up with its error message. It first fixed the type of the method reference, then went to look for such a method. In another example the message may have hit the spot, but in your case it happened to mislead you. – Marko Topolnik Aug 28 '15 at 07:21

3 Answers3

18

Because you referred to it as

MethodRefTest::passMeAround

You should have referred to it as

this::passMeAround

providing the instance to use for context.

A different way to view your current code is to say that MethodRefTest::passMeAround is a BiFunction<MethodRefTest, String, String> and this lambda shape does not match the target site. So, alternatively, you may have written

public BiFunction<MethodRefTest, String, String> testReferences() {
  return MethodRefTest::passMeAround;
}

and in the main method

final MethodRefTest t = new MethodRefTest();
t.testReferences().apply( t, "foo" );

The material you have been reading may not be instructive enough, so let me expand. With

this::passMeAround

you get a lambda object which has captured the this instance from the context where it was created, but with

MethodRefTest::passMeAround

you get a "pure", non-capturing lambda which represents the invocation of the specified method on some instance, with some string argument, so when you apply it, you need to pass in both.

Marko Topolnik
  • 195,646
  • 29
  • 319
  • 436
  • I was following this https://blog.idrsolutions.com/2015/02/java-8-method-references-explained-5-minutes/ example. – Benjamin Aug 27 '15 at 16:17
  • Some more explanation is in the "Method References" section of http://www.studytrails.com/java/java8/Java8_Lambdas_FunctionalProgramming.jsp – cxw Aug 27 '15 at 16:17
0

Change passMeAround to static:

 public static String passMeAround( String input ) {
      return input + " been passed to me";
   }

or refer to it as to instance method:

 public Function<String, String> testReferences() {
      final Function<String, String> f1 = this::passMeAround;
      return f1;
 } 
ka4eli
  • 5,294
  • 3
  • 23
  • 43
  • 2
    "Change passMeAround to static" doesn't solve nor explain anything Why `MethodRefTest::passMeAround` doesn't work? (notice that according to https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html `ContainingType::methodName` is "Reference to an *instance method* of an arbitrary object of a particular type") – Pshemo Aug 27 '15 at 16:17
-1

To answer your question about why the context is static, it's because the part before the :: was the name of a class rather than the name of an instance. If you don't have an instance, only static members are accessible.

cxw
  • 16,685
  • 2
  • 45
  • 81
  • 1
    But `MethodRefTest::passMeAround` is a legal method reference even though it refers to an instance method. It's just not appropriate at the place where OP used it. Method reference is not a member access expression. – Marko Topolnik Aug 27 '15 at 16:39