33

In Java 8 there is "Method Reference" feature. One of its kind is "Reference to an instance method of an arbitrary object of a particular type"

http://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html#type

Can someone explain what does "arbitrary object of particular type" mean in that context ?

Roman Ivanov
  • 2,477
  • 3
  • 20
  • 31
  • 1
    Of particular type = a String, or an Integer, or a MyClass, or... Arbitrary object = some instance of the type, for example, for type String, "abf" or "another string". – assylias May 08 '14 at 07:20

7 Answers7

30

The example given from the Oracle Doc linked is:

String[] stringArray = { "Barbara", "James", "Mary", "John", "Patricia", "Robert", "Michael", "Linda" };
Arrays.sort(stringArray, String::compareToIgnoreCase);

The lambda equivalent of

 String::compareToIgnoreCase

would be

(String a, String b) -> a.compareToIgnoreCase(b)

The Arrays.sort() method is looking for a comparator as its second argument (in this example). Passing String::compareToIgnoreCase creates a comparator with a.compareToIgnoreCase(b) as the compare method's body. You then ask well what's a and b. The first argument for the compare method becomes a and the second b. Those are the arbitrary objects, of the type String (the particular type).

Don't understand?

  • Make sure you know what a comparator is and how to implement it.
  • Know what a functional interface is and how it affects lambdas in Java.
  • A comparator is a functional interface that's why the method reference becomes the body of the compare method inside the comparator object.
  • Read the source below for another example at the bottom of the page.

Read more at the source: http://moandjiezana.com/blog/2014/understanding-method-references/

Jawad
  • 313
  • 5
  • 8
20

It is a reference to an instance method from some type. In the case of the example, compareToIgnoreCase is a method from String. The program knows that it can invoke this method on an instance of String, so it can take the reference and any object of that type and be guaranteed the method exists.

I would compare this to the Method class in that they refer to a method and can be invoked on an arbitrary instance of some type.

For the example, it can use two String objects and call compareToIgnoreCase on one and use the other as an argument to match the method signature. This allows it to take the array and sort it based on any method of the array type instead of requiring a comparator instance to do this instead.

And here is the example for anyone who didn't click on the link in the question:

String[] stringArray = { "Barbara", "James", "Mary", "John",
"Patricia", "Robert", "Michael", "Linda", "George" };
Arrays.sort(stringArray, String::compareToIgnoreCase);
Andrew Vitkus
  • 827
  • 7
  • 9
6

Please see the below code sample which explains "Reference to an Instance Method of an Arbitrary Object of a Particular Type" category described in https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html

import java.util.Arrays;

class Person{
    String name;

    //constructor
    public Person(String name){
        this.name = name;
    }

    //instance method 1
    public int personInstanceMethod1(Person person){
        return this.name.compareTo(person.name);
    }

    //instance method 2
    public int personInstanceMethod2(Person person1, Person person2){
        return person1.name.compareTo(person2.name);
    }
}

class Test {
    public static void main (String[] args) throws Exception{
        Person[] personArray = {new Person("A"), new Person("B")};
    
        // Scenario 1 : Getting compiled successfully
        Arrays.sort(personArray, Person::personInstanceMethod1);
    
        // Scenario 2 : Compile failure
        Arrays.sort(personArray, Person::personInstanceMethod2);
    
        // Scenario 3 : Getting compiled successfully. 
        Person personInstance = new Person("C");
        Arrays.sort(personArray, personInstance::personInstanceMethod2);

        // Scenario 4 : Getting compiled successfully. As the same way as "Scenario 1"
        String[] stringArray = { "Barbara", "James", "Mary", "John",
                                "Patricia", "Robert", "Michael", "Linda" };
        Arrays.sort(stringArray, String::compareToIgnoreCase);
    }

}

Scenario 1 and Scenario 4 describes "Reference to an Instance Method of an Arbitrary Object of a Particular Type" category described in https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html

If the method parameter takes a variable in same instance Type as the instance Type of the element, you can call that instance method using Type.(Person::personInstanceMethod1)

Compare "personInstanceMethod1" instance method in "Person" class with "compareToIgnoreCase" instance method in "String" class (https://docs.oracle.com/javase/8/docs/api/java/lang/String.html#compareToIgnoreCase-java.lang.String-) to see the similarity. Both are taking a single parameter with the same Type.

Compare Scenario 1 and Scenario 2 to see the difference.

Viraj
  • 71
  • 1
  • 3
  • 1
    Nice work, both creating a your own example class and methods plus illustrating the crucial differences between the three different scenarios. – JL_SO Jan 02 '19 at 23:02
  • 1
    very nicely explained Viraj !! – Mayur Jan 17 '19 at 05:22
  • I was scratching my head for the longest time why it was possible to pass String::concat as a comparator without referencing a particular String object. So the syntax for a reference to an instance method of an arbitrary object of a particular type is "ContainingType::methodName". So, just to be clear, the type of methodname's parameters must exactly match the type of ContainingType, correct? So if you defined a class Person2 with the exact same "instance method 1" as class Person, then Arrays.sort(personArray, Person2::personInstanceMethod1) will fail to compile? – Nicholas Cousar Jul 22 '21 at 06:31
6

I think this is a case where understanding the concept is made harder by the use of undefined terminology on the documentation.

Keep in mind that a method reference, as a type of lambda, implements a functional interface, and that ANY method that matches the signature of the interface can be used as long as you can reference it in your program.

Of the four kinds of method references, which represent different ways needed to access (reference) the method. The syntax of three are pretty straightforward: If it is a static method then you use the class name before the :: operator, if it is an instance method in an object then you generally use a reference variable for the object, or if it a constructor you use ClassName::new.

The fourth kind is where you want to call a method that is an instance method of a parameter passed to the function. With a lambda, there is no problem because you can reference the parameter variable, like this:

(String someString) -> someString.toLowerCase();

However, since there is no explicit parameter variable in a method reference, the syntax used instead is:

String::toLowerCase;

The compiler takes the "particular type" (String) to reference the functional method (toLowerCase) contained in an "arbitrary object" (the object passed in the parameter). The term "arbitrary object" is used because the actual object passed in the parameter could be different each time the method reference is executed.

Steve McCollom
  • 121
  • 1
  • 3
  • Thanks! What's confusing about method reference is the fact that the rule "ANY method that matches the signature of the interface can be used" can be broken IE: take an Optional, Product obj. contains field "price" I can say optional.map(Product::getPrice) map method defined as: Optional map(Function super T, ? extends U> mapper) Meaning "U" can be any new type, but the parameter type of the Function MUST be Product. Yet method referenced (getPrice) takes 0 args as it's a getter method. So the signatures don't match, that rule is broken. Yet it still compiles ??? – Keith Dec 07 '22 at 04:18
  • The "instance passed to the function" part is what made it click for me. Cause it looks like you're calling a static method... except you're not, so how does it know what instance to call? But it makes sense because the instance is known when it gets passed in. – wilmol May 25 '23 at 09:14
0

Let me put it another way. So, if your lambda expression looks like the following:

(<ContainingType arg>, <otherArgs>) -> arg.instanceMethod(<otherArgs>)

It can be replaced with method reference such as

ContainingType::instanceMethod

So, for the lambda expression

(String a, String b) -> a.compareToIgnoreCase(b)

It can be replaced with method reference such as

String::compareToIgnoreCase

Here, the particular type is the ContainingType which is String. And an instance of it (String) is arbitrary since we haven't declared or initialized it yet and these are simply the arguments here. And hence "arbitrary object of particular type" in this context is "arbitrary object of String type"

Laks
  • 244
  • 2
  • 7
0

Everyone is using the same String::compareToIgnoreCase example. To better understand this consider following example with all reference types togather:

public class Test {
    public static void main(String[] args) {

        Consumer<String> con = str-> StringPrinter.staticPrint(str);
        //using static method ref
        con = StringPrinter::staticPrint; 

        StringPrinter prtr = new StringPrinter();
        con = str-> prtr.instancePrint(str);
        //using instance method ref
        con = prtr::instancePrint;
        

        BiConsumer<StringPrinter, String> biCon = (pp,str)->pp.instancePrint(str);
        //using instance method ref of an arbitrary object of particular type
        biCon = Printer::instancePrint; //notice stringPrinter object of Printer type
        
        //constructor ref
        Supplier<StringPrinter> sup = StringPrinter::new; 
    }
}


interface Printer {
    public void instancePrint(String msg);
}

class StringPrinter implements Printer{
    public static void staticPrint(String msg) {
        System.out.println("Static: " + msg);
    }

    public void instancePrint(String msg) {
        System.out.println("Instance: " + msg);
    }
}
the Hutt
  • 16,980
  • 2
  • 14
  • 44
-1

In this case there is an array of objects of a particular type(String) and any random object in array can call its instance method . This approach allows a class to refer to its instance method as if it is a static method .

Also this approach works only for built in class of java like String but not for user defined class . In case of user defined class the instance method can only be referred by its object .