Java 8 has introduced a lot of new features such as lambda expressions, stream, Method references etc.
In this post, we will see what are Method references and how can we use it. I will try to provide more examples rather than theory.
Table of Contents
1. Introduction to Method Reference
Method references
are special types of lambda expressions that execute only one method.
General syntax of method reference:
1 2 3 |
object::methodName |
You might have already guessed that you need to understand lambda expressions first. If you are comfortable with lambda expressions, then let’s move forward.
Let’s understand this with the help of example:
Let’s create class MethodReferecesLambdaExpressionMain
in which we are going to print list using stream’s foreach method.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
import java.util.Arrays; import java.util.List; import java.util.function.Consumer; public class MethodReferecesLambdaExpressionMain { public static void main(String args[]) { List<String> countryList=Arrays.asList(new String[] {"India", "China","Nepal","Russia"}); System.out.println("======================="); System.out.println("Using anonymous class"); System.out.println("======================="); // Using anonymous class countryList.stream().forEach( new Consumer<String>() { @Override public void accept(String country) { System.out.println(country); } }); System.out.println("======================="); System.out.println("Using lambda expression"); System.out.println("======================="); // Using lambda expression countryList.stream().forEach( country -> System.out.println(country) ); System.out.println("======================="); System.out.println("Using Method references"); System.out.println("======================="); // Using method reference countryList.stream().forEach( System.out::println ); } } |
Output:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
======================= Using anonymous class ======================= India China Nepal Russia ======================= Using lambda expression ======================= India China Nepal Russia ======================= Using Method references ======================= India China Nepal Russia |
stream.foreach()
method takes consumer functional interface as agrument.
Consumer is functional interface that takes a single argument and returns nothing.
We have used consumer functional interface in 3 ways.
- Using anonymous class
12345678910Consumer<string> consumer1 = new Consumer<string>() {@Overridepublic void accept(String country) {System.out.println(country);}};</string></string> - Using lambda expression
1234Consumer<string> consumer2 = country -> System.out.println(country);</string> - Using method reference
1234Consumer<string> consumer3 = System.out::println;</string>
You might already know that you can use lambda expression instead of an anonymous class, but You can use method reference only when the lambda expression just calls to a method.
So if you look at below syntax:
1 2 3 4 |
Consumer<string> consumer3 = System.out::println; </string> |
In method reference, we have Class or object before ::
and method name after ::
without arguments.
Did you notice the method reference does not have arguments?
Yes, we don’t need to pass arguments to method reference, arguments are passed automatically internally based on type of method reference.
The below diagrams will make it clearer.
You can use method reference as below:
Let’s say you want to convert country
to uppercase
before printing it. You can achieve it using anonymous class
and lambda expression
but not with method reference.
You can not use method reference as below:
You can obviously use the stream’s map() method to convert the country
to uppercase
before printing it. I just want to demonstrate when method reference can’t be used.
2. Types of method references
There are four types of method references.
- Reference to static method
- Reference to instance method of object type
- Reference to instance method of existing object
- Reference constructor
2.1 Reference to static method
When you have lambda expression which calls to static method, then you can method reference to static method.
Lambda expression syntax
(args) -> ClassName.someStaticMethod(args)
can be converted to
ClassName::someStaticMethod
Let’s see this with the help of example.
Create a class name PowerFunctions
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
import java.util.ArrayList; import java.util.List; import java.util.function.Function; class PowerFunctions { // This is the method we will call in method reference public static Integer power(int a) { return a*a; } // Function is functional interface which will be target for method reference public static List<Integer> calculatePowOf2ForList(List<Integer> list, Function<Integer,Integer> function) { List<Integer> powerNumbers = new ArrayList<>(); for(Integer num:list) { Integer powOf2 = function.apply(num); powerNumbers.add(powOf2); } return powerNumbers; } } |
Function is functional interface that takes a single input T and returns a single output R.
We can call calculatePowOf2ForList()
as below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
import java.util.Arrays; import java.util.List; import java.util.function.Function; public class StaticMethodReferenceMain { public static void main(String args[]) { List<Integer> list=Arrays.asList(new Integer[] {1,2,3,4,5}); // using anonymous class Function<Integer,Integer> function1=new Function<Integer, Integer>() { @Override public Integer apply(Integer num) { return PowerFunctions.power(num); } }; List<Integer> calculatePowForList1 = PowerFunctions.calculatePowOf2ForList(list, function1); System.out.println(calculatePowForList1); // Using lambda expression Function<Integer,Integer> function2 = (num) -> PowerFunctions.power(num); List<Integer> calculatePowForList2 = PowerFunctions.calculatePowOf2ForList(list, function2); System.out.println(calculatePowForList2); // Using Method reference Function<Integer,Integer> function3 = PowerFunctions::power; List<Integer> calculatePowForList3 = PowerFunctions.calculatePowOf2ForList(list, function3); System.out.println(calculatePowForList3); } } |
When you run above program, you will get below output:
1 2 3 4 5 |
[1, 4, 9, 16, 25] [1, 4, 9, 16, 25] [1, 4, 9, 16, 25] |
If you notice,Function<Integer,Integer> function2 = (num) -> PowerFunctions.power(num);
is of type (args) -> className.someStaticMethod(args)
Here,
PowerFunctions
is className- someStaticMethod is
power
method num
is power method argument.
We are calling a static method power
of class PowerFunctions
in lambda expression, that’s why we can use it as method reference.
So instead of
Function<Integer,Integer> function2 = (num) -> PowerFunctions.power(num);
we can use
Function<Integer,Integer> function3 = PowerFunctions::power;
Here,
- First type parameter of
Function
(Integer) is first parameter of static methodpower()
. - Second type parameter of
Function
(Integer) is return type of static methodpower()
.
2.2 Reference to instance method of object type
When you have lambda expression where instance of object is passed and calls to an instance method with/without parameters, then you can use method reference to an instance method with object type.
Lambda expression syntax
(obj,args) -> obj.someInstanceMethod(args)
can be converted to
objectType::someInstanceMethod
Let’s see this with the help of example.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
import java.util.function.BiFunction; public class MethodReferenceObjectType { public static void main(String[] args) { // Using anonymous class BiFunction<String,Integer,String> bf1=new BiFunction<>() { @Override public String apply(String t, Integer u) { return t.substring(u); } }; String subString1 = getSubstring("Java2blog",2,bf1); System.out.println(subString1); // Using lambda expression BiFunction<String,Integer,String> bf2 = (t,u) -> t.substring(u); String subString2 = getSubstring("Java2blog",2,bf2); System.out.println(subString2); // Using Method reference BiFunction<String,Integer,String> bf3 = String::substring; String subString3 = getSubstring("Java2blog",2,bf3); System.out.println(subString3); } public static String getSubstring(String str1,int beginIndex,BiFunction<String,Integer,String> p) { return p.apply(str1, beginIndex); } } |
BiFunction
is functional interface that takes two arguments and returns single output.
If you notice,BiFunction<String,Integer,String> bf2 = (t,u) -> t.substring(u);
is of type (obj,args) -> obj.someInstanceMethod(args)
Here
obj
is of type String.- someInstanceMethod is String’s
substring()
method. - args is
beginIndex
forsubstring()
method argument.
So BiFunction<String,Integer,String> bf2 = (t,u) -> t.substring(u);
can be converted to
BiFunction<String,Integer,String> bf3 = String::substring;
Here,
- First
BiFunction
parameter type(String) is String object itself. - Second
BiFunction
parameter type(Integer) is argument tosubstring()
method - Third
BiFunction
parameter type(String) is return type ofsubstring()
method
2.3 Reference to instance method of existing object
When you have lambda expression where instance of object is used to call an instance method with/without parameters, then you can use method reference to an instance method with an existing object.
Lambda expression syntax
(args) -> obj.someInstanceMethod(args)
can be converted to
objectType::someInstanceMethod
Here obj
is defined somewhere else and is not part of argument to lambda expression.
Let’s understand with the help of example.
Create a class named Country.java
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
public class Country { String name; long population; Country(String name) { this.name=name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public long getPopulation() { return population; } public void setPopulation(long population) { this.population = population; } @Override public String toString() { return "[ name = "+name+" population = "+population+" ]"; } } |
Create another class MethodReferenceExistingObjectMain.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
import java.util.function.Consumer; public class MethodReferenceExistingObjectMain { public static void main(String[] args) { Country c=new Country("India"); // Using anonymous class Consumer<Long> popCons1=new Consumer<Long>() { @Override public void accept(Long t) { c.setPopulation(t); } }; popCons1.accept(20000L); System.out.println(c); // Using Lambda expression Consumer<Long> popCons2= (population) -> c.setPopulation(population); popCons2.accept(30000L); System.out.println(c); // Using method reference Consumer<Long> popCons3 = c::setPopulation; popCons3.accept(40000L); System.out.println(c); } } |
Output:
1 2 3 4 5 |
[ name = India population = 20000 ] [ name = India population = 30000 ] [ name = India population = 40000 ] |
Consumer is functional interface which takes single argument and returns nothing.
If you notice, Consumer
is of type (args) -> obj.someInstanceMethod(args)
Here
- obj is of type Country and declared somewhere else.
- someInstanceMethod is Country’s setPopulation method.
- args is
population
for setPopulation method argument.
So Consumer<Long> popCons2= (population) -> c.setPopulation(population);
can be converted to Consumer<Long> popCons3 = c::setPopulation;
Here,
- First Consumer parameter type(Long) is argument to setPopulation method.
2.4 Reference constructor
When lambda expression is used to create new object with/without parameters, then you can use reference method constructor.
Lambda expression syntax
(args) -> new ClassName(args)
can be converted to
ClassName::new
Let’s see with the help of example.
Here we will convert the list to set using method reference.
Function<List
is functional interface which will take a list an argument and will return set by calling HashSet constructor public HashSet(Collection<? extends E> c)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.function.Function; public class MethodReferenceConstructorMain { public static void main(String[] args) { ArrayList<String> list=new ArrayList<>(); list.add("Rohan"); list.add("Andy"); list.add("Sneha"); list.add("Rohan"); // Anonymous class Function<List<String>,Set<String>> f1= new Function<List<String>, Set<String>>() { @Override public Set<String> apply(List<String> nameList) { return new HashSet<>(nameList); } }; Set<String> set1 = f1.apply(list); System.out.println(set1); // Using lambda expression Function<List<String>,Set<String>> f2 = (nameList) -> new HashSet<>(nameList); Set<String> set2 = f2.apply(list); System.out.println(set2); // Using Method reference Function<List<String>,Set<String>> f3= HashSet::new; Set<String> set = f3.apply(list); System.out.println(set); } } |
Output:
1 2 3 4 5 |
[Sneha, Andy, Rohan] [Sneha, Andy, Rohan] [Sneha, Andy, Rohan] |
If you notice, Function<List<String>,Set<String>> f2 = (nameList) -> new HashSet<>(nameList);
is of type (args) -> new ClassName(args)
Here
- args is of type
list
- ClassName is
HashSet
So Function<List<String>,Set<String>> f2 = (nameList) -> new HashSet<>(nameList);
can be converted to Function<List<String>,Set<String>> f3= HashSet::new;
Here,
- First
Function
parameter type(List) is argument to HashSet constructor.
3. Excercise
Let’s practice few exercises based on Method reference.
3.1 Excercise:1
Given a list of Integer, you need to find square root of each number in the list and return it as List<Double>.
You need to call Math’s sqrt
static method to find square root of number and use it as method reference
.
3.2 Excercise:2
Given a [list of Strings](https://java2blog.com/list-string-java/ “list of Strings”), you need to convert all the String to uppercase
and then return a new list
.
3.3 Excercise:3
Given a list of Color objects, you need to sort them of color’s name and return a sorted list of color names(List
)
Here is the definition of Color class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
public class Color { String name; String htmlCode; public Color(String name, String htmlCode) { super(); this.name = name; this.htmlCode = htmlCode; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getHtmlCode() { return htmlCode; } public void setHtmlCode(String htmlCode) { this.htmlCode = htmlCode; } @Override public String toString() { return "Color [name:"+name+" HtmlCode:"+htmlCode+"]"; } } |
4. Conclusion
Method references are special type of lambda expression that simply calls a method. It makes code more readable and concise, but if method references lead to any confusion, then there is no point in using them.
That’s all about method references in java.
Good nice website, covering almost everything