Java 8 introduced many new features that simplify and improve the way we write code. In simpler terms, a method reference allows you to use a method without invoking it. It is helpful while using lambda expressions since it enhances the code’s comprehensibility. Understanding when and how to use method references can improve your Java programming skills when functional programming is required.
This blog post aims to explore what a method reference is in Java 8, why it is helpful, and the various types that exist. Each type will be explained in practical terms and examples provided so that one can learn how to apply such in real-world projects.
Prerequisites to Understand Method Reference in Java 8
To fully grasp the concept of method references, it’s essential to understand some foundational concepts in Java: methods, functional interfaces, and lambda expressions. These are key components that form the basis for method references. Let’s explore each concept.
Methods in Java
A method is a block of code that when executed, a specific task is performed. Methods can return a value or be void (no return). They can take parameters or be parameterless. In Java, methods are defined within classes and are called using objects or class names.
Example:
public class MathUtils {
public int add(int a, int b) {
return a + b;
}
}
Method references allow us to use a currently existing method just as an argument to the function.
Functional Interface
When we have only one abstract method in the interface then it is the functional interface. This type of interface can represent a single task or function. We can see different in-built functional interfaces which are provided by Java 8. For example, functional interfaces like Runnable, Callable, Comparator, and more. Also with the help of using the @FunctionalInterface annotation, we can create our own functional interface.
Example:
@FunctionalInterface
interface MathOperation {
int operation(int a, int b);
}
Method references work seamlessly with functional interfaces. When a lambda expression implements a functional interface, a method reference can be used instead of the lambda to provide a more straightforward, readable implementation.
Lambda Expressions
Lambda expressions are anonymous functions that provide a clear and concise way to represent single-method interfaces (functional interfaces). They simplify writing small, quick functions and are often used to pass behaviour as an argument to higher-order functions.
Lambda expressions can often be replaced by method references, making code even more compact. For example, you can use a method reference to achieve the same result when a lambda calls a method directly.
Understanding the Relationship
To use method references effectively, you need to understand the method and learn how functional interfaces and lambda expressions work together. Method references are just shorthand for lambda expressions that call existing methods. This reference points to a method by its name instead of defining a function inline, leading to more readable code. Here’s how they relate:
Methods provide reusable code blocks.
Functional Interfaces define the single method.
Lambda Expressions and Method References are ways to implement the method defined in a functional interface.
By understanding these relationships, you are prepared to apply method references in Java 8 correctly. Let’s dive directly into understanding the distinct varieties of method references and the way to use them in your code.
Get curriculum highlights, career paths, industry insights and accelerate your technology journey.
Download brochure
What is a Method Reference in Java 8?
A method reference in Java 8 is a shorthand notation of a lambda expression to call a method. Method references employ the symbol ‘::’ in order to distinguish the method name itself from its owner class or any other object. Such references can be made to static methods or instance methods including constructors.
Syntax of Method Reference
The syntax for method reference depends on the type of method being referred to. The general form of method reference looks like this:
Reference to a Static Method:
ClassName::staticMethodName
Example of Method Reference
Let’s see an easy example that shows how method references work. Suppose you’ve got a list of strings and you want to sort them using the compareToIgnoreCase method of the String class:
Here, in this lambda expression (s1, s2)->s1.compareTolgnoreCase(s2), we can use the method reference, which is String::compareTolgnoreCase. Thus, replacing it makes the code more readable and precise.
Types of Method Reference in Java 8
Method references in Java 8 can be classified into four main types:
Method reference to an instance method of a particular object of a class
Static method reference
Method reference to an instance method of an arbitrary object of a specific type
Method reference to a constructor
Now, let’s explore each type with detailed explanations and examples.
Method reference to an instance method of a particular object of a class
This method references are typically used when you need to call an instance method of any specific object. This will allow you to use a reference to an existing object. It calls one of its methods directly instead of writing the whole lambda expression. The main benefit of this is it will simplify the code through directly referencing the instead method.
Syntax
The syntax for referencing an instance method of a particular object is:
objectName::instanceMethodName
objectName is the reference to the existing object whose method you want to refer to.
instanceMethodName is the name of the method that belongs to the object.
Example 1: Method Reference to an Instance Method of a Particular Object
Suppose you have a Printer class with an instance method printMessage(). You want to use this method with a lambda expression. Instead of writing a lambda, you can use a method reference.
Example:
import java.util.function.Consumer;
public class Printer {
public void printMessage(String message) {
System.out.println("Printing: " + message);
}
public static void main(String[] args) {
Printer printer = new Printer(); // Create an instance of Printer
// Using method reference to an instance method of a particular object
Consumer<String> messagePrinter = printer::printMessage;
// Executing the method reference
messagePrinter.accept("Hello, World!"); // Output: Printing: Hello, World!
}
}
Explanation:
Here, we have a Printer class with an instance method printMessage(String message).
We create an object printer of the Printer class.
We use a method reference printer::printMessage to refer to the printMessage method of the printer object.
The Consumer<String> functional interface is used to accept a single input argument and perform an operation.
accept(“Hello, World!”) calls the printMessage method, displaying “Printing: Hello, World!”.
Example 2: Using Method Reference with List forEach
Let’s see another example where we use method references with the forEach method of a list.
Example:
import java.util.Arrays;
import java.util.List;
public class Printer {
public void display(String item) {
System.out.println("Item: " + item);
}
public static void main(String[] args) {
Printer printer = new Printer(); // Create an instance of Printer
List<String> items = Arrays.asList("Apple", "Banana", "Orange");
// Using method reference to an instance method of a particular object
items.forEach(printer::display); // Output: Item: Apple, Item: Banana, Item: Orange
}
}
Explanation:
In this example, we have a method display(String item) in the Printer class that prints an item.
We create an instance printer of the Printer class.
A list items contains some strings (“Apple”, “Banana”, “Orange”).
The forEach method of the list is used to iterate over each item, and the method reference printer::display is passed to forEach.
The method reference is used to print each item in the list.
By using a method reference, we avoid writing a lambda expression like (item) -> printer.display(item), making the code shorter and more readable.
Static Method Reference
Static methods belong to the class itself rather than to any specific object. When you need to use a static method in a lambda expression, you can use this type of method reference to simplify the code. Here, we do not need an instance of the class to call a static method. We can simply use static method reference directly to refer to the static method in any class.
Syntax
The syntax for referencing a static method is: ClassName::staticMethodName
ClassName is the name of the class that contains the static method.
staticMethodName is the name of the static method you want to refer to.
Example 1: Static method Reference for Mathematical Calculations
Here, we can create an example in which we will be using a static method reference to do a simple mathematical calculation.
Example:
import java.util.function.BiFunction;
public class Calculator {
public static int add(int a, int b) {
return a + b;
}
public static void main(String[] args) {
// Using static method reference
BiFunction<Integer, Integer, Integer> addition = Calculator::add;
// Executing the method reference
int result = addition.apply(5, 3);
System.out.println("Result: " + result); // Output: Result: 8
}
}
Explanation:
The Calculator class has a static method add(int a, int b) that returns the sum of two integers.
We use a static method reference Calculator::add to refer to the static add method.
The BiFunction functional interface takes two input arguments and produces a result.
The addition.apply(5, 3) method call invokes the add method, resulting in “Result: 8”.
Example 2: Static Method Reference for Utility Operations
Consider another example where we use a static method reference for a utility operation, such as converting a string to an integer.
Example:
import java.util.function.Function;
public class StringUtils {
public static int toInteger(String s) {
return Integer.parseInt(s);
}
public static void main(String[] args) {
// Using static method reference
Function<String, Integer> stringToInteger = StringUtils::toInteger;
// Executing the method reference
int number = stringToInteger.apply("123");
System.out.println("Converted Number: " + number); // Output: Converted Number: 123
}
}
Explanation:
The StringUtils class has a static method toInteger(String s) that converts a string to an integer using Integer.parseInt.
We use a static method reference StringUtils::toInteger to refer to the static toInteger method.
The Function functional interface takes one input argument and produces a result.
The stringToInteger.apply(“123”) method call invokes the toInteger() method, resulting in “Converted Number: 123”.
Method reference to an instance method of an arbitrary object of a specific type
You must use this type of method when you have a collection of objects and you don’t know the specific instances in advance but want to apply an instance method to each object. Moreover, it can act as an even more powerful tool while working with collections and streams because it can work with instance methods of any object of a specific type.
Syntax
The syntax for referencing an instance method of an arbitrary object is: ClassName::instanceMethodName
ClassName is the name of the class whose method you want to refer to.
instanceMethodName is the name of the instance method.
Example 1: Method Reference with Stream for Data Manipulation
Let’s look at an example where we use a method reference to manipulate a stream of strings.
Example:
import java.util.Arrays;
import java.util.List;
public class Example {
public static void main(String[] args) {
List<String> names = Arrays.asList("John", "Alice", "Bob", "Steve");
// Using method reference to an instance method of an arbitrary object of a specific type
names.stream().map(String::toUpperCase).forEach(System.out::println);
// Output: JOHN, ALICE, BOB, STEVE
}
}
Explanation:
In this example, we have a list of names.
And, to convert each name to uppercase, we have the map method of the Stream interface that applies the String::toUpperCase method reference to each element in the list.
The forEach(System.out::println) method reference prints each uppercase name to the console.
Example 2: Sorting a List with Method Reference
Now, let’s use this type of method reference to sort a list of strings in a case-insensitive manner.
Example:
import java.util.Arrays;
import java.util.List;
public class Example {
public static void main(String[] args) {
List<String> names = Arrays.asList("John", "alice", "Bob", "steve");
// Using method reference to an instance method of an arbitrary object of a specific type
names.sort(String::compareToIgnoreCase);
names.forEach(System.out::println);
// Output: alice, Bob, John, steve
}
}
Explanation:
We have a list of names with varying cases.
The sort method sorts the list using the String::compareToIgnoreCase method reference.
The compareToIgnoreCase method is applied to each pair of strings in the list, ensuring a case-insensitive sort.
Finally, the sorted names are printed using forEach(System.out::println).
Method Reference to a Constructor
A method reference to a constructor is used when you want to refer to a constructor to create new instances. It provides a more compact way to create objects compared to explicitly using the new keyword in a lambda expression. This type of method reference is handy when you need to instantiate objects within a stream or in any scenario where objects are created dynamically.
Syntax
The syntax for referencing a constructor is: ClassName::new
ClassName is the name of the class whose constructor you want to refer to.
new is the keyword used to reference the constructor.
Example 1: Using Constructor Reference with Supplier
Suppose you have a Person class, and you want to create new instances of this class using a constructor reference.
Example:
import java.util.function.Supplier;
class Person {
private String name;
public Person() {
this.name = "Unknown";
}
public String getName() {
return name;
}
}
public class Example {
public static void main(String[] args) {
// Using constructor reference to create a new Person instance
Supplier<Person> personSupplier = Person::new;
Person person = personSupplier.get();
System.out.println("Person Name: " + person.getName()); // Output: Person Name: Unknown
}
}
Explanation:
The Person class has a no-argument constructor that initialises the name field to “Unknown”.
The Supplier<Person> interface is a functional interface that supplies new Person objects.
We use the constructor reference Person::new to refer to the Person constructor.
The personSupplier.get() method call creates a new Person instance, and “Person Name: Unknown” is printed.
Example 2: Using Constructor Reference with List and Stream
Let’s create an example where we use a constructor reference to create a list of objects.
Example:
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
class Car {
private String model;
public Car(String model) {
this.model = model;
}
public String getModel() {
return model;
}
}
public class Example {
public static void main(String[] args) {
// Using constructor reference to create a list of Car objects
List<Car> cars = Stream.of("Tesla", "BMW", "Audi")
.map(Car::new)
.collect(Collectors.toList())
cars.forEach(car -> System.out.println("Car Model: " + car.getModel()));
// Output: Car Model: Tesla, Car Model: BMW, Car Model: Audi
}
}
Explanation:
The Car class has a constructor that takes a string argument (model).
We use the Stream.of method to create a stream of car models.
The map(Car::new) method reference uses the constructor reference to create a new Car object for each model in the stream.
The collect(Collectors.toList()) method collects the new Car objects into a list.
Finally, each car’s model is printed using a lambda expression in forEach.
Why is Method Reference Important?
Method references are important in Java 8 for several reasons:
Improves Code Readability: The use of method references improves the readability of the code as it avoids the complexity of coding and sources of diagrams. It helps build slanging methods rather than creating tiny margins.
Reduces Code Size: Rather than writing a lambda expression, you can avoid doing so and employ a method reference which is shorter, and easier. This helps in saving the text length of your code and helps in better understanding.
Supports Functional Programming: Method references align with the functional programming style introduced in Java 8. They allow you to pass behaviour (methods) as parameters, which is a core concept in functional programming.
Optimised Performance: Method references can be optimised by the Java compiler, which might lead to better performance. They also provide a more natural way to use functional interfaces.
Promotes Reusability: Method references provide a way to reuse existing methods and, thus prevent rewriting new code for operations that are already defined somewhere.
Conclusion
In conclusion, method references in Java 8 are a great way of enhancing the code and making it easier to read and maintain. With method references, you will replace the need to use lambda expressions wherever a method reference can be used to call a shorter code.
It’s always good to know what types of method references are available so that you can select the one that best fits the application task and helps improve the functionality of the Java applications you create.
This way of implementing method references encourages more of the functional programming approach that came with the changes in Java 8. Whatever the situation, whether it is static methods, instance methods or constructors, there is a method reference that is effective and straightforward in the expression of code.
FAQs
What is a method reference in Java 8?
A shorthand notation to call methods by referring to them, instead of using lambda expressions.
How many types of method references are there in Java 8?
Four: instance method of a particular object, static method, instance method of an arbitrary object, and constructor.
What is the benefit of using method references?
They make code more concise, readable, and maintainable, reducing boilerplate code.
Can method references replace all lambda expressions?
No, method references can only replace lambdas that directly call an existing method.
Are method references faster than lambda expressions?
Performance is similar; method references mainly offer code readability and simplicity.
What is the syntax for referencing a static method?
ClassName::staticMethodName
Can you use method references with any functional interface?
Yes, as long as the method signature matches the abstract method of the functional interface.
Hero Vired is a leading LearnTech company dedicated to offering cutting-edge programs in collaboration with top-tier global institutions. As part of the esteemed Hero Group, we are committed to revolutionizing the skill development landscape in India. Our programs, delivered by industry experts, are designed to empower professionals and students with the skills they need to thrive in today’s competitive job market.