Java8 : All about Method reference


Java8 brings many features which help us to code like Functional programming in an OO context. One of the important and useful features is method reference, In this article, we will do a detailed discussion about method reference.

What is a Method reference?
In Java8, Method reference is a concept, by which we can point a method, But think about OO context if we need to use a method of another class what we generally do, we compose that Object -- HAS-A relationship/or pass that Object as a parameter,  into a method then call the desired method by "." operator <instance.methodName()>, but think-- To use just one method we have to carry the whole object, it would be great if we can directly pass that method like a variable. By method reference, we can do the same.

How does Method reference work?
By:: operator, If we invoke a method, it gives us the method refrence, But think in OO context, we can't do any operation without an Object, and Java is an OO language so how we can pass only a method, The answer is -- java8 Functional Interface, It is an interface with one and only one abstract method,  Java8 use that interface concept intelligently, we know by an interface we can polymorphically accept any implementation so If we think, method reference as a Functional interface  and method matching this Functional interface is an implementation then we can pass any method which matches the signature of Functional interface.In simple term when we invoke a method using:: it wraps that method in a Functional interface, and the only abstract method of the Functional interface inferred the type and signature from the method, its referenced by::. So any functional Interface whose signature match with the method signature of the invoked method is a potential candidate for hold the method reference, similarly, any method whose signature match with the Functional interface signature can be invoked or passed.

Let see a simple example,

package com.example.methodref;
import java.util.function.Consumer;
public class Utility {
public void display(String content,Consumer<String> disPlayMedium){
disPlayMedium.accept(content);
}
public void displayIntoProjector(String content,Projector projector){
projector.display(content);
}
public Projector createProjector() {
return new Projector() {
@Override
public void display(String content) {
System.out.println(content);
}
};
}
public static void main(String[] args) {
Utility utility = new Utility();
Projector projector = utility.createProjector();
utility.display("Hello Shamik using Lambda", content->System.out.println(content));
utility.display("Hello Shamik Using Method refrence", System.out::println);
utility.display("Hello Shamik Using Projector Functional Interface", projector::display);
utility.displayIntoProjector("Hello Shamik using Lambda", content->System.out.println(content));
utility.displayIntoProjector("Hello Shamik using Methodrefrence", System.out::println);
utility.displayIntoProjector("Hello Shamik using explicit Projector", projector::display);
}
}

Deep dive into the Code :
Here I create a simple program, which displays a message, for the same I create a display method which takes a message and a medium where the message should be displayed like the console, projector etc.
The display method takes a string, and a consumer functional interface, Consumer's accept method takes a string and returns nothing. So we can invoke display method using lambda expression which takes an argument and print that.
utility.display("Hello Shamik using Lambda", content->System.out.println(content));
But wait, think about the method signature of println(), it takes a string and return nothing/void, so it's signature match with Consumer 's accept method so as per method reference if any method signature match with functional interface signature we can use method reference so we easily use method reference to println
our code look like,
utility.display("Hello Shamik Using Method reference", System.out::println);
look here I am not passing any parameter to a printing method, so the obvious question is what it should print the answer lies in the method display
public void display(String content,Consumer<String> disPlayMedium){
disPlayMedium.accept(content);
}
in that method, we actually apply the display medium to the content we pass,  by method reference we defer the call and apply the call when required unlike the method call using. operator.
Now, we want to display our content into a projector so we will create a projector interface like following.

package com.example.methodref;
public interface Projector {
public void display(String content);
}

One thing is noticeable here, the signature of the display method of projector interface is same as Consumer interface accept method. as Utility class display method takes Consumer as an argument we can pass the Projector's display method as a reference so we can write the following

utility.display("Hello Shamik Using Projector Functional Interface", projector::display);
In the same way, now we create another method called displayIntoProjector which takes content and the projector interface as a display medium. so we use our own Projector interface rather than using Java8 Consumer interface, but notice that we can call this method same as we called the display method, so Our own interface also acts as a functional interface and can hold method reference.

Till, this point we have a fair understanding How Method reference works, Now we will discuss the type of method reference.
There are 4 types of method reference
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.

1. Reference to a static method:If we need to pass a reference to a static method we can do so, Signature of the static method should be matched with the Functional interface.
see the below example

package com.example.methodref;
import java.util.function.Consumer;
public class Utility {
public void display(String content, Consumer<String> disPlayMedium) {
disPlayMedium.accept(content);
}
public static void writeOnPapaer(String content){
System.out.println("On paper ::" + content);
}
public static void main(String[] args) {
Utility utility = new Utility();
utility.display("Hello Shamik Using static method reference", Utility::writeOnPapaer);
}
}

Here I have written one static method called writeOnPaper whose signature is same as Consumers accept, so we can invoke the writeOnPaper using Utility::writeOnPapaer.

2.Reference to an instance method of a particular object:Sometimes we need to pass a method as a method reference which holds a state of it's enclosing Object, so that method is holding a state of an Outside variable/ member variable, So we have to use that particular object instance when the method invoked by another method, So it is utmost important to pass that Object context also.
Corresponding lambda would be
(object)->object.someMethod(object)
so, In a simple term, if we need to reference a method which is not stateless, in that case, we will call that method using the same instance so that state will be preserved.
see the example,

package com.example.methodref;
import java.util.function.Consumer;
import java.util.function.Supplier;
public class Utility {
private String fixedContent ="Hello this is a default content";
public void changeTheStateOfFixedContent(String content){
fixedContent = content;
}
public String displayFixedContent() {
return fixedContent;
}
public static void main(String[] args) {
Utility utility = new Utility();
utility.changeTheStateOfFixedContent("Changing the State");
Supplier<String> supplier = utility::displayFixedContent;
System.out.println(supplier.get());
Supplier<String> newsupplier = new Utility()::displayFixedContent;
System.out.println(newsupplier.get());
}
}

See, the example carefully here I have a fixedcontent which will be returned by the method displayFixedContent, Now I add another method called changeTheStateOfFixedContent by which we change thebody of fixedContent i.e changing the state.
Now in main when I create a Utility Object instance and change the fixed Content by invoking utility.changeTheStateOfFixedContent("Changing the State");
after that, I use same object instance when I invoke the displayFixedContent method as a reference SO that changed content is preserved. and when I do the call supplier.get() I got the output Changing the State which was set earlier.
But look the second call here I create a new Utility Object and invoke the displayFixedContent method as the reference. The new Object does not know about the changed state so it shows default message Hello this is default a content.
So the crux is when a method uses a state/member variable of the enclosing Object, we should use that particular object to invoke the method as a reference.
3. Reference to an instance method of an arbitrary object of a particular type :  If an instance method is stateless, that is it does not use any member variable which holds a particular state then we can use any reference of that class to invoke the method as a reference because we want to reuse that method and method does not hold any state.

like the display method in Utility class it does not hold any member so invoke the same we can use any object instance of Utility let see the example

package com.example.methodref;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Supplier;
public class Utility {
public void display(String content, Consumer<String> disPlayMedium) {
disPlayMedium.accept(content);
}
public static void main(String[] args) {
Utility utility = new Utility();
BiConsumer<String,Consumer<String>> biConsumer = utility::display;
biConsumer.accept("Hi This is a Test", System.out::println);
BiConsumer<String,Consumer<String>> biConsumerAgain = new Utility()::display;
biConsumerAgain.accept("Hi This is a Test", System.out::println);
}
}

See that as the display is stateless in both cases we got the same output "Hi This is a Test".
4. Reference to a constructor:  we can use constructor as a method reference also, By doing this we not create the Object immediately unlike new SomeObject(), we defer the Object creation and when the Object is actually required we create them. Now if you look the signature of new you see it does not take any argument but produce an instance of a class, so it acts as Java8 Supplier interface which takes nothing but returns an Object, So we can use Supplier as the reference to the constructor.

Let see the example

import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Supplier;
public class Utility {
private String fixedContent ="Hello this is default content";
public String displayFixedContent() {
return fixedContent;
}
public static void main(String[] args) {
Supplier<Utility> utilitySupplier = Utility::new;
String body = utilitySupplier.get().displayFixedContent();
System.out.println(body);
}
}


Conclusion: Method reference is an important concept, By the same, we can pass a method into a higher order method without passing the Object as a whole.

Java8:Effective use of Consumer Functional Interface

Java8 provides a functional interface called Consumer, Whose signature looks like following

@FunctionalInterface
public interface Consumer<T> {

    void accept(T t);

    default Consumer<T> andThen(Consumer<? super T> after) {
       Objects.requireNonNull(after);
       return (T t) -> { accept(t); after.accept(t); };
    }
}

So, consumer has one default method andThen and one abstract method accept. I will discuss  the andThen later, let's concentrate on accept method.

Seeing the signature of accept, one thing is clear it takes an argument and produce nothing so it returns type is void. So the consumer is logically opposite of Supplier-- which takes nothing but produce something. I can say Produces can start a flow and Consumer ends that flow, Consumer acts as a terminal operation which can complete a method chain.

By use of method reference feature in java8, we can reference any method whose signature matches the accept method of Consumer so we can refer that method as a consumer functional interface.

Let see a simple example where I want to create and display the mail (Don’t do this in production it breaks SRP, create and display should be two separate methods  It is just for example purpose).

Very basic code will look like following

package com.example.consumer;

import java.io.StringWriter;
import java.util.function.Consumer;

public class EmailManager {
   
   public void createAndDisplayEmail(String emailBody){
      StringWriter writer = new StringWriter();
      writer.append("Hi,");
      writer.append("\n");
      writer.append(emailBody);
      writer.append("\n");
      writer.append("Thanks, Shamik");
      System.out.println(writer.toString());
     
   }
   public static void main(String[] args) {
      EmailManager manager = new EmailManager();
      manager.createAndDisplayEmail("This is a mail from Javaonfly");
   }

}



Output :
Hi
This is a mail from Javaonfly
Thanks, Shamik


Nothing fancy, The createAndDisplayEmail method takes the body content then add salutation and footer to the body after that it prints the email.

As the method signature of the createAndDisplayEmail match with Consumer’s accept method signature, we can use java8 method reference and hold the reference of the createAndDisplayEmail in Consumer functional interface so we can invoke the method later when required, to achieve that we just we need to add following code snippet in the main method.


Consumer<String> consumer = manager::createAndDisplayEmail;
consumer.accept("This is a mail from Javaonfly");

After seeing the above code an inquisitive mind immediately ask two questions
1. What is the benefit of holding the reference in Consumer functional interface,
2. How that is differ from the call manager.createAndDisplayEmail("This is a mail from Javaonfly") apart from syntax.


There are several benefits by doing that, first, let me answer  the second question
How that differs from the call manager.createAndDisplayEmail("This is a mail from Javaonfly") apart from syntax?

prior to Java8, we don’t have privilege to treat method as variable so we can’t pass a method into another method as an argument like we usually do in case of variable, to achieve the same we have to pass an Object which holds that method, so to reuse the method either we have to make it static or has to pass the Object . Which is OK but it has a problem unnecessary we have to carry the context(i.e Object) while we need only the method definition. In Java 8 now we can easily hold the method reference using appropriate interface without context, and reuse the method later.

Now come to the first question,
What is the benefit of holding the reference in Consumer functional interface?

Consumer as a Strategy Pattern:

As we can hold the reference of a method in Consumer Functional interface and Consumer is an interface so we can have multiple implementations--- the question is, what are those implementation then, as Consumer holds method reference we can say it holds any methods whose method signature is matched with Consumer, and they can act as Strategy-- based on the context we can pass different method reference in to higher-order method.  so lower level methods act as an implementation of the strategy.

Hard to digest? let understand it through our Mail example

Suppose, Now we have to add new functionality into our EmaiManager class, Requirements is like that, as Javaonfly company has several departments When we send the mail that should have department specific formats and in the footer section we have to change the signature based on department.

So it is obvious the salutation, footer, the format will be changed dynamically based on the department, How we can implement the same.

Prior to Java 8, we will create an interface which has footer, salutation and format method and based on department we will implement several classes based on the department, and in EmaiManager class in createandDisplay method, we will pass the interface and email body as arguments and based on department we will pass actual implementation.

So, we need to create multiple classes, But same can be achieved very short and crisply way using Consumer interface. The Consumer is a predefined interface and can hold methods reference we will create various overloaded/separate methods based on department and then create a Higher-order function which takes Consumer interface and email body, then apply the consumer upon email body.

Let see the code

package com.example.consumer;

import java.io.StringWriter;
import java.util.function.Consumer;

public class EmailManager {
   
   
   
   public void createAnddisplayEmail(String emailBody){
      StringWriter writer = new StringWriter();
      writer.append("Hi,");
      writer.append("\n");
      writer.append(emailBody);
      writer.append("\n");
      writer.append("Thanks, Shamik");
      System.out.println(writer.toString());
     
   }
   
   public void createAnddisplayAdminEmail(String emailBody){
      StringWriter writer = new StringWriter();
      writer.append("Hi,");
      writer.append("\n");
      writer.append(emailBody);
      writer.append("\n");
      writer.append("Thanks, Admin");
      System.out.println(writer.toString());
     
   }
   
   public void createMailandDisplay(Consumer<String> consumer,String mailBody){
      consumer.accept(mailBody);
   }
   public static void main(String[] args) {
      EmailManager manager = new EmailManager();
      String body ="This is a mail from Javaonfly";
      Consumer<String> directorEmailConsumer = manager::createAnddisplayEmail;
      Consumer<String> adminEmailConsumer = manager::createAnddisplayAdminEmail;
      manager.createMailandDisplay(directorEmailConsumer, body);
      manager.createMailandDisplay(adminEmailConsumer, body);
     
   }

}

If you look at the code minutely you will find there is a higher order function called createMailandDisplay which takes mail body and consumer which act as Higher order class in the Strategy pattern and the methods createAndDisplayEmail and createAnddisplayAdminEmail are the actual implementation of the strategy which were pass from main method.

Consumer as a Filter Chain Pattern :
As Consumer takes one input and produce nothing, so it behaves like a terminal operation, but sometimes we need to apply series of actions on the input.  Generally in OO design we do it through FilterChain where we will create an interface with a method doFilter() then implements multiple classes based on the action which we apply on the input, also create a FilteManger class which iterate over the filters and apply the filter on input, or a filter apply itself then delegate the call to next filter. The Same concept can be achieved through Consumer interface where each filter implementation represents a method and a Higher-order function treat as Filter manager which will iterate over methods and apply it to the input.

Let try to solve a problem using Filter pattern,

Suppose before creating and sending any mail we want to apply spell checking, grammar checking and format checking on the email body, so that email body is error-free and well formatted. How we can achieve the same using consumer.

Obvious there is a series of actions spell check, grammar check will be applied to email body so we can use Filter pattern there.

Let see the code

package com.example.consumer;

import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;


public class EmailManager {
   
   public void spellChecker(String emailBody){
      System.out.println("Spell Checking on :: " + emailBody);
     
   }
   public void grammarChecker(String emailBody){
      System.out.println("Grammar Checking on :: " + emailBody);
     
   }
   public void formatChecker(String emailBody){
      System.out.println("Format Checking on :: " + emailBody);
     
   }
   
   public void applyFilter(List<Consumer<String>> filterList,String emailBody){
      filterList.forEach(filter->filter.accept(emailBody));
   }
   
   public static void main(String[] args) {
      EmailManager manager = new EmailManager();
      String body ="This is a mail from Javaonfly";
      List<Consumer<String>> filterList=new ArrayList<Consumer<String>>();
      filterList.add(manager::spellChecker);
      filterList.add(manager::grammarChecker);
      filterList.add(manager::formatChecker);
      manager.applyFilter(filterList, body);
     
   }

}



Ouput ::
Spell Cheking on :: This is a mail from Javaonfly
Grammar Cheking on :: This is a mail from Javaonfly
Format Cheking on :: This is a mail from Javaonfly


Here, I have to create a List and add the specific consumer which act as a filter, alternatively, I can use andThen functionality of the Consumer, Which takes another consumer as an input then returns a consumer which apply the consumer creates it then apply the consumer which passed as an argument. We can use this functionality which is same as filter chain.

Lets look more sophisticated version,



package com.example.consumer;

import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

public class EmailManager {
   
   public void spellChecker(String emailBody){
      System.out.println("Spell Cheking on :: " + emailBody);
     
   }
   public void grammarChecker(String emailBody){
      System.out.println("Grammar Cheking on :: " + emailBody);
     
   }
   public void formatChecker(String emailBody){
      System.out.println("Format Cheking on :: " + emailBody);
     
   }
   

   public void applyFilter(Consumer<String> composeFilter,String emailBody){
      composeFilter.accept(emailBody);
   }

   public static void main(String[] args) {
      EmailManager manager = new EmailManager();
      String body ="This is a mail from Javaonfly";
      Consumer<String> filter = manager::spellChecker;
      Consumer<String> composeFilter = filter.andThen(manager::grammarChecker).andThen(manager::formatChecker);
      manager.applyFilter(composeFilter, body);
     
   }

}




Conclusion : Consumer has multiple utilities you can use it for crisp coding and use of Method reference along with java8 defined interface we can rewrite our OOPs design pattern in a shorter and crisp way , but as now behaviour stuffed in methods, Its breaks the SRP, because all the methods now in one class and class has many reason to change.