Method reference in Java 8
-
Last Updated: August 30, 2023
-
By: javahandson
-
Series
In this article, we will learn about method reference and the type of method reference in Java 8 with proper examples. If you want to learn about other Java 8 topics you can click the above Java 8 menu or you can click on this link Java 8 topics.
Method reference lets us reuse the existing method definition and pass them just like lambdas. In other words, we can say method reference can let us create a lambda expression from an existing method implementation.
Using method reference we can make our code more readable and precise. Method references are the shorthand for lambdas calling only a specific method.
Say we are having a Test Class that has an addition method that adds 2 integers.
package com.javahandson.method.reference; public class Test { public static void addition(int a, int b) { System.out.println(a+b); } }
Now say we have a functional interface that has an add method which should also add 2 integers.
@FunctionalInterface interface Operation { void add(int a, int b); }
We will implement the above functional interface to add 2 integers.
public class Demo { public static void main(String[] args) { Operation operation = (int a, int b) -> System.out.println(a+b); operation.add(10, 15); } }
From the above example, we can see Test Class addition method does the same thing that the above lambda expression is doing. So why to unnecessarily create a lambda implementation again?
Instead, our lambda expression can make use of the existing addition method definition of Test Class. This is possible with method reference.
When we need a method reference then the target reference is placed before the delimiter :: and the method name is provided after it.
Target reference can be a Class or an instance.
Class-Name :: Method-Name Instance-Name :: Method-Name
We will learn more about them in the below section.
Method reference can be of 2 types:
1. Static method reference
2. Instance method reference
We will be referring to the existing static method instead of creating a new one. Here we can call the reference method using the Class name.
In the above example addition method of the Test Class is a static method hence we can call that method using the Test Class.
public class Demo { public static void main(String[] args) { Operation operation = Test::addition; operation.add(10, 15); } } Output: 25
We have removed the extra piece of duplicate code i.e. we have removed the lambda expression and we are referring to the existing piece of code.
Since addition is a static method of Test Class that is the reason we have referred addition method using Test Class.
Here the add method of Operation interface is referring to the addition method of the Test Class. Hence the term method reference is used.
Test::addition Here no bracket is required after the method name because we are not actually calling the method we are just referring to it.
We should use the method reference if the implementation we require exists for our lambda expression. If the implementation we require is not present in any of the existing Class then we should create our own new lambda expression.
We will be referring to the existing instance method instead of creating a new one. Here we can call the reference method using the instance name.
We will create a Test Class with an instance method i.e. addition.
package com.javahandson.method.reference; public class Test { void addition(int a, int b) { System.out.println(a+b); } }
Now we will use the existing addition method of Test Class using the instance method reference.
package com.javahandson.method.reference; @FunctionalInterface interface Operation { void add(int a, int b); } public class Demo { public static void main(String[] args) { Test test = new Test(); Operation operation = test::addition; operation.add(10, 15); } } Output : 25
In the above example since the addition method of Test Class is an instance method that is the reason we are creating the Test Class object first and then using its instance name, we are referring to its addition method.
Here the add method of Operation interface refers to the instance addition method of the Test Class.
1. We can maintain a concise codebase.
2. A lot of boilerplate code is removed.
3. The readability of the codebase increases.
4. We can simplify the codebase. Instead of writing a new complex lambda altogether, we are using the existing code.
1. The reference method return type can be different from the one in the functional interface.
We will create a method with a return type as int.
package com.javahandson.method.reference; public class Test { public static int addition(int a, int b) { return a+b; } }
Now we will create a functional interface with a method whose return type is float.
package com.javahandson.method.reference; @FunctionalInterface interface Operation { float add(int a, int b); }
The addition method of the Test Class has a different return than the add method of the Operation interface. Still, we can use the addition method of Test Class as a method reference for the add method of Operation interface.
public class Demo { public static void main(String[] args) { Operation operation = Test::addition; operation.add(10, 15); } } Output: 25.0
2. The reference method access modifier can be different from the one in the functional interface.
@FunctionalInterface interface Operation { float add(int a, int b); } public class Demo { private static int addition(int a, int b) { return a+b; } public static void main(String[] args) { Operation operation = Demo::addition; System.out.println(operation.add(10, 15)); } } Output: 25.0
The functional interface has a method with a default access modifier as public whereas the addition method of Demo Class to which the lambda expression is referring to has an access modifier as private.
Note* The method reference works fine in both the above 2 cases.
3. The reference method arguments cannot be different from the one in the functional interface. If the argument mismatches then Java will throw a compile time exception.
package com.javahandson.method.reference; public class Test { public static int addition(int a, int b) { return a+b; } }
package com.javahandson.method.reference; @FunctionalInterface interface Operation { int add(int a, int b, int c); } public class Demo { public static void main(String[] args) { Operation operation = Test::addition; System.out.println(operation.add(10, 15)); } } Output: java: incompatible types: invalid method reference method addition in class com.javahandson.method.reference.Test cannot be applied to given types required: int,int found: int,int,int reason: actual and formal argument lists differ in length
The functional interface has a method with 3 arguments whereas the addition method of Test Class which the lambda expression is referring to has only 2 arguments hence a compile time exception is thrown.
So this is all about method reference in Java 8. I hope you like the article. If you are having any questions on this topic please raise them in the comments section.