Method Reference in Java Lmbda expressions

Sameera De Silva
6 min readJan 21, 2024

--

Method reference is a special type of lambda expression . It only executes one method. It can have parameters or no parameters.

syntax-

Here . methodname is the method call, object, is the reference to call that method

object::methodname

1. Special Type of Lambda Expression:

Method references are indeed a concise way to create lambda expressions.
They leverage existing methods, eliminating the need to write explicit lambda bodies.

2. Executes One Method:
A method reference doesn’t execute the method immediately.
It creates a reference to the method, which is executed when the functional interface is invoked.

3. Parameters:
Method references can handle methods with or without parameters.
The specific syntax for the reference depends on the type of method being referenced.
4. Types of Method References:

Static Method Reference: Refers to a static method of a class (e.g., ClassName::methodName).

Instance Method Reference of a Particular Object: Refers to an instance method of a specific object (e.g., object::methodName).
Instance Method Reference of an Arbitrary Object: Refers to an instance method of any object of a particular type (e.g., ClassName::methodName).
Constructor Reference: Refers to a constructor of a class (e.g., ClassName::new).

Benefits of Method References:

Conciseness: They make code more compact and readable by directly referring to methods instead of writing full lambda expressions.
Type Safety: The compiler can infer the type of the method reference, reducing potential errors.
Expressiveness: They often convey intent more clearly than explicit lambda bodies.

Now let’s go though a coding example to understand these concepts.

. Static Method Reference:

In this section, we explore static method references in Java 8, focusing on the Pizza class. Key points include:

  • Demonstrating how to call static methods directly using the class name.
  • Exploring the syntax for method references with and without parameters.
  • Utilizing the built-in Consumer interface for method references.
  • Creating a custom functional interface (PizzaShopNameSetter) and using it for method references.
  • Applying method references to static methods with the Supplier interface.

2. Non-Static Method Reference:

This section delves into non-static method references, specifically those related to the setPizzaOrder and getPizzaOrder methods. Highlights include:

  • Creating an instance of the Pizza class and setting pizza orders using Java 7 syntax.
  • Introducing the method reference syntax for non-static methods (object::non-staticMethodName).
  • Demonstrating the use of custom functional interfaces (PizzaOrderSetter) for method references.
  • Employing the built-in BiConsumer interface for non-static method references.
  • Displaying the result of the getPizzaOrder method.

3. Instance Method of Arbitrary Object:

This section explores method references for arbitrary objects of a particular type, focusing on the getPizzaOrder method. Key points include:

  • Using the Function interface for method references on an arbitrary Pizza object.
  • Applying method references to instance methods with the apply method.
  • Creating an arbitrary Pizza object and calling the instance method using the method reference.

4. Constructor Method Reference:

The final section showcases constructor method references in Java 8. Highlights include:

  • Traditional object creation using the default constructor.
  • Introducing the syntax for constructor references (ClassName::new).
  • Demonstrating the use of constructor references with the built-in Supplier interface.
  • Creating instances of the Pizza class using constructor references.
  • Highlighting the functional equivalence of constructor references and traditional object creation.

This code provides a comprehensive overview of method references in Java 8, covering static methods, non-static methods, instance methods of arbitrary objects, and constructor methods. It serves as a valuable resource for understanding and implementing method references in various scenarios.

This is the class that creates Pizza.

package com.exercise.lambdamethodreference;

public class Pizza {
static String pizzaShopName;
boolean WithCheese;
int howManyPizzas;

public static void setPizzaShopName(String pizzaShopName){
Pizza.pizzaShopName =pizzaShopName;

}
public static String getPizzaShopName(){
return pizzaShopName;
}

public void setPizzaOrder(boolean WithCheese,int howManyPizzas) {
this.WithCheese = WithCheese;
this.howManyPizzas = howManyPizzas;
}
public String getPizzaOrder(){
String order= "Your order is Pizza count "+howManyPizzas +" With cheese "+ WithCheese;
return order;
}
}

Here are the interfaces that I wrote.

Interface to set the pizza order.

package com.exercise.lambdamethodreference;



@FunctionalInterface
interface PizzaOrderSetter {
void accept(boolean withCheese, int howManyPizzas);
}

Interface to set the pizza shop name.

package com.exercise.lambdamethodreference;

@FunctionalInterface
public interface PizzaShopNameSetter {
void accept(String pizzaShopName);

}

Interface to get the pizza shop name.

package com.exercise.lambdamethodreference;

@FunctionalInterface
public interface PizzaMethodsStaticReference {
String get();
}
package com.exercise.lambdamethodreference;

import org.testng.annotations.Test;

import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;

public class MainMethodReferenceHome {

@Test(priority = 1, description = "Static method reference with and without parameters.")
public void staticMethodExample() {
// We can call static methods directly using class name
Pizza.setPizzaShopName("La Tienda de Pizza Italiano");

/* Method reference syntax for static class is follows ,
ClassName:: Static method name
Pizza::setPizzaShopName();

Unfortunately, you cannot directly use method references in this way for methods that have arguments.
The Consumer interface's accept method takes one argument,
so you need to pass the argument separately when calling accept.
pizzaShopNameSetter.accept("Best Pizza Shop")
*/
// Using inbuilt Consumer interface lets set a value.
// Using method reference to call setPizzaShopName
Consumer<String> pizzaShopNameSetter = Pizza::setPizzaShopName;
// Calling the method reference
pizzaShopNameSetter.accept("Best Pizza Shop");
// Using the custom functional interface to call setPizzaShopName.
PizzaShopNameSetter pizzaShopNameSetterCustom = Pizza::setPizzaShopName;
// Calling the accept method
pizzaShopNameSetterCustom.accept("Custom Pizza Shop");

// Accessing the static variable
System.out.println("Pizza Shop Name: " + Pizza.pizzaShopName);

// Accessing the static variable
System.out.println("Pizza Shop Name: " + Pizza.pizzaShopName);

/* Method reference syntax for static class is follows ,
ClassName:: Static method name
Pizza::getPizzaShopName();
*/


// We can call static methods directly using class name
String pizzaHutName = Pizza.getPizzaShopName();
System.out.println("pizza Hut Name==" + pizzaHutName);

// Using the custom functional interface to get a static method which don't have parameters.
PizzaMethodsStaticReference staticReference = Pizza::getPizzaShopName;
// Calling the static method using the reference
String pizzaShopName = staticReference.get();
System.out.println("Pizza Shop Name: " + pizzaShopName);

// Using method reference for a static method using inbuilt interface Supplier
Supplier<String> staticReferenceInbuilt = Pizza::getPizzaShopName;
System.out.println(staticReferenceInbuilt.get());
}


@Test(priority = 2, description = "Instance method reference")
public void nonStaticMethodExample() {
// Now let's call the non-static method by creating an object. In Java 7 way
Pizza pizza = new Pizza();
pizza.setPizzaOrder(true, 2);

/* In Java 8, the Method reference syntax for a non-static method is as follows:
object::non-staticMethodName
Here, it would be: pizza::setPizzaOrder
Parameters are provided later when calling the method.
*/

// Using method reference for setPizzaOrder method with the user-defined PizzaOrderSetter interface.
PizzaOrderSetter customFunctionalInterfaceOrderSetter = pizza::setPizzaOrder;

// Calling the setPizzaOrder method using the custom functional interface
customFunctionalInterfaceOrderSetter.accept(true, 2);


// Using method reference for setPizzaOrder method with inbuilt BiConsumer interface.
BiConsumer<Boolean, Integer> methodReferenceOrderSetter = pizza::setPizzaOrder;
methodReferenceOrderSetter.accept(true, 1);

System.out.println(pizza.getPizzaOrder());
}

@Test(priority = 3, description = "Instance method reference - Arbitrary Object of Particular Type")
public void instanceMethodOfArbitraryObject() {
// Using method reference for an arbitrary object of a particular type
Function<Pizza, String> arbitraryInstanceMethodReference = Pizza::getPizzaOrder;

// Creating an arbitrary Pizza object
Pizza anotherPizza = new Pizza();
anotherPizza.setPizzaOrder(false, 3);

// Calling the instance method using the reference
String orderDetails = arbitraryInstanceMethodReference.apply(anotherPizza);
System.out.println(orderDetails);
/*
Function<Pizza, String>: This is a functional interface from the java.util.function package.
It represents a function that takes an argument of type Pizza and produces a result of type String.
In this case, it's used for the method reference Pizza::getPizzaOrder.

arbitraryInstanceMethodReference:
This variable is of type Function<Pizza, String>, indicating that it can reference a method that
takes a Pizza object and returns a String.

Pizza::getPizzaOrder: This is the method reference.
It is a shorthand notation for a lambda expression that takes a Pizza object and calls its getPizzaOrder method.

arbitraryInstanceMethodReference.apply(anotherPizza):
This line applies the function, invoking the method reference on the anotherPizza object. The result is stored in the orderDetails variable.

System.out.println(orderDetails): Finally, the order details are printed to the console.
*/
}

@Test(priority = 4, description = "Constructor method reference")
public void constructorMethodExample() {
// Normal way of creating an instance using the constructor
Pizza normalPizza = new Pizza();
normalPizza.setPizzaOrder(true, 1);
System.out.println(normalPizza.getPizzaOrder());

// Using constructor reference with inbuilt interface Supplier
Supplier<Pizza> constructorReference = Pizza::new;

// Creating a Pizza object using the constructor reference
Pizza newPizza = constructorReference.get();
/*
get method is part of the Supplier interface, and it is used to obtain a new instance of a Pizza object by invoking the default constructor through a constructor reference.
This approach is concise and aligns with the functional programming features introduced in Java 8.
*/
newPizza.setPizzaOrder(false, 2);
System.out.println(newPizza.getPizzaOrder());

/*
We use the Pizza::new syntax to create a constructor reference for the Pizza class.
The Supplier<Pizza> functional interface is used here, which represents a supplier of objects.
We then call get() on the Supplier to create a new instance of Pizza using the constructor reference.

is this Pizza newPizza is same as Pizza normalPizza = new Pizza();

Yes, the expression Pizza newPizza = constructorReference.get();
is functionally equivalent to Pizza normalPizza = new Pizza();.
Both expressions create a new instance of the Pizza class using its default constructor.
*/

}
}

Further readings

https://www.youtube.com/watch?v=5rbdwovjbw4 https://www.baeldung.com/java-method-references

--

--

No responses yet