6

Lets say we have the following class:

public class NameCreator {

    public String createName(String lastname) {
        return lastname;
    }

    public String createName(String lastname, String firstName) {
        return lastname + " " + firstname
    }

    ...
}

If I want to call it via Java 8 method reference:

NameCreator::createName

I will get the error:

Cannot resolve method createName

How can I define which of those methods I want to be called?

Lorelorelore
  • 3,335
  • 8
  • 29
  • 40
Mulgard
  • 9,877
  • 34
  • 129
  • 232

3 Answers3

11

NameCreator::createName implies that either the method is static (kind #1 in the table below), or that the functional interface target also takes an instance of the class (kind #3, for example
BiFunction<NameCreator, String, String>). Your methods are not static, and so presumably your target does not take an instance, which is why you get the "Cannot resolve method" error. You probably want to use the method reference on an instance (kind #2). From within the class, you can use:

Function<String, String> func = this::createName

From outside the class you can use:

NameCreator creator = new NameCreator();
Function<String, String> func = creator::createName;

As to whether the one- or two-parameter version is used, it depends on what functional interface is being targeted. The above will use your first method, because Function<String, String> takes a String and returns a String. The following functional interface, as an example, would use your second method:

NameCreator creator = new NameCreator();
BiFunction<String, String, String> func = creator::createName;

See: Function, BiFunction, and the whole java.util.function package

You may also be interested in the Java tutorial on method references, specifically this part:


There are four kinds of method references:

Kind                                                   | Example
==============================================================================================
Reference to a static method                           | ContainingClass::staticMethodName
-------------------------------------------------------+--------------------------------------
Reference to an instance method of a particular object | containingObject::instanceMethodName
-------------------------------------------------------+--------------------------------------
Reference to an instance method of an arbitrary object | ContainingType::methodName
of a particular type                                   |
-------------------------------------------------------+--------------------------------------
Reference to a constructor                             | ClassName::new
==============================================================================================
Michael
  • 41,989
  • 11
  • 82
  • 128
  • Perfect. So what I can do is: `Consumer consumer = creator::createName`. Or `Function consumer = ...` and so on. Thank you. – Mulgard Jul 13 '18 at 10:19
  • Yep. If you don't care about the resulting strings, you can use `Consumer` or `BiConsumer`, depending on which method you want. Parameter lists always need to match exactly, but return types can also be ignored. (see [my answer here](https://stackoverflow.com/questions/45460896/why-do-consumers-accept-lambdas-with-statement-bodies-but-not-expression-bodies/45461066#45461066)) – Michael Jul 13 '18 at 10:21
  • 1
    You can reference instance methods with `NameCreator::createName` too, they don't have to be static. It just means that they take an extra first argument which represents the instance. e.g. `BiFunction func = NameCreator::createName;` would reference the first method. – Jorn Vernee Jul 13 '18 at 10:29
  • @JornVernee True, it was an oversimplification. Thanks, I've updated my answer. Hopefully its more comprehensive now. – Michael Jul 13 '18 at 10:34
4

Method references rely on inference. So without proper context from which the compiler can infer the target functional interface, that resolution error is raised.

You should assign it to an interface type that declares a method matching the signature (or use it in a context where the target type is defined, such as a method argument).

For example:

interface INameCreator {
    String create(String name);
}

interface INamesCreator {
    String create(String firstName, String lastName);
}

And then you can use the method references:

//match NameCreator.createName(String)
INameCreator creator = this::createName //within the class
INameCreator creator = nameCreatorInstance::createName 

And

//match NameCreator.createName(String, String)
INamesCreator creator = this::createName //within the class
INamesCreator creator = nameCreatorInstance::createName

If the method were static, you would be able to use the NameCreator::createName syntax in the same context.

ernest_k
  • 44,416
  • 5
  • 53
  • 99
0

If you want to create instances of type NameCreatorbased on Strings, use this:

 public static class NameCreator {

    public static String createName(String lastname) {
        return lastname;
    }

    public static String createName(String lastname, String firstName) {
        return lastname + " " + firstName;
    }
}

and then make calls like:

  List<String> items = new ArrayList<>();
    //
    items.forEach(NameCreator::createName);//uses the first method
    Map<String, String> map = new HashMap<>();
    //
    map.forEach(NameCreator::createName); //uses the second method
Mustapha Belmokhtar
  • 1,231
  • 8
  • 21