Java 8 Method References
Double colon (: 🙂 operator
Starting from Java 8, there was introduced a new feature, called method reference. In short, it is a special type of lambda expressions and often is used to create simple lambda expressions by referencing existing methods. Each time you can just replace your lambda expression with method reference.
Of course, this feature is being widely used because of its benefits. One of the benefits is that by using method references, you make your code shorter, which can become more readable.
➢ For example, let us have an output like this.
str -> System.out.println(str)
This line can be replaced with a method reference, and we can write it.
System.out::println
Here the :: operator separates the Class or the Object from the method.
➢ Another example we can discuss now.
Here we have an anonymous class, which is just printing a list.
List<Integer> myList = Arrays.asList(1, 2, 3, 4);
//this is an anonymous class
myList.forEach(new Consumer<Integer>() {
@Override
public void accept(Integer a) {
System.out.println(a);
}
});
In its turn, this anonymous class can be replaced with Lambda expressions.
List<Integer> myList2 = Arrays.asList(1, 2, 3, 4);
myList2.forEach(a -> System.out.println(a));
This Lambda expressions can be changed into Method references.
List<Integer> myList3 = Arrays.asList(1, 2, 3, 4);
myList3.forEach(System.out::println);
So, this is the hierarchy.
Anonymous Class -> Lambda expression -> Method Reference
The method references can be of four types:
• Static methods (Class::staticMethodName)
• Instance methods of particular objects (object::instanceMethodName)
• Instance methods of an arbitrary object of a particular type (Class::methodName) • Constructor (Class::new)
So, now let us discuss each of them and look at examples.
• Static Method References
When calling a static method with its containing class before, you simply refer a static method defined in that class. The syntax that describes this process of referring is below:
ContainingClass::staticMethodName Â
Now let us define a class and refer its static method from another class, and describe this process. Here is a class with its static method:
public class A {
public static void duplicateNumber(int num) {
int duplicate = num * 2;
System.out.println(duplicate);
}
}
And below we are using static utility method:
public class B {
public static void main(String[] args) {
List<Integer> integerList = new LinkedList<>();
integerList.add(3);
integerList.add(9);
integerList.add(8);
integerList.add(14);
//anonymous class
integerList.forEach(new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
A.duplicateNumber(integer);
}
});
//lambda expression
integerList.forEach((a) -> A.duplicateNumber(a));
//method reference
integerList.forEach(A::duplicateNumber);
}
}
The result for this code will be:
6
18
16
28
So the method references always utilize the :: operator.
• Instance methods of particular objects
When you call a method from a particular instantiated object using the reference variable of the object, you just reference the method.
Here is the syntax describing this process:
containingObject::instanceMethodName
Now we can look at an example of referring the instance method.
Here is a Person class:
public class Person {
private String name;
private String surname;
private int age;
// Constructor, getters and setters
@Override
public String toString() {
return “Name: ” + name + “, Surname: ” + surname + “, Age: ” + age;
}
public int compareTo(Person person) {
if (this.age <= person.age) {
return 1;
} else {
return –1;
}
}
}
And now we define OurComparator:
public class OurComparator {
public int compareEntities(Person per1, Person per2) {
return per1.compareTo(per2);
}
}
And finally we create a list and sort it:
public static void main(String[] args) {
Person per1 = new Person(“Gayane”, “Zakoyan”, 20);
Person per2 = new Person(“Harut”, “Aristakesyan”, 22);
Person per3 = new Person(“Ann”, “Adams”, 45);
Person per4 = new Person(“John”, “Simons”, 7);
ArrayList<Person> people = new ArrayList<>();
people.add(per1);
people.add(per2);
people.add(per3);
people.add(per4);
// Initializing our CustomComparator
OurComparator ourComparator = new OurComparator();
//anonymous class
people.sort(new Comparator<Person>() {
@Override
public int compare(Person person, Person person2) {
return ourComparator.compareEntities(person, person2);
}
});
// lambda expression
Collections.sort(people, (obj1, obj2) -> ourComparator.compareEntities(obj1, obj2));
// Instead of making a call to an arbitrary Person
// we’re now providing an instance and its method
//method reference
Collections.sort(people, ourComparator::compareEntities);
System.out.println(people);
}
The result is:
[Name: Ann, Surname: Adams, Age: 45, Name: Harut, Surname: Aristakesyan, Age: 22, Name: Gayane, Surname: Zakoyan, Age: 20, Name: John, Surname: Simons, Age: 7]
A difference between these is that by OurComparator we can add many functionalities for comparison and take away from the class.
• Instance methods of an arbitrary object of a particular type
This type of method referencing is similar to the one discussed above, but here we do not have to create an object to perform the comparison.
Here is the syntax for this type of method referencing:
Class::methodName
Now let us create an Integer list that we are going to sort.
List<Integer> integerList = new ArrayList<>();
integerList.add(9);
integerList.add(15);
integerList.add(3);
integerList.add(7);
integerList.add(1);
integerList.add(18);
Here if we use a classic lambda expression, both of the parameters should be passed, but if we use a method reference it will look like this:
//anonymous class
integerList.stream().sorted(new Comparator<Integer>() {
@Override
public int compare(Integer integer, Integer integer2) {
return integer.compareTo(integer2);
}
});
//lambda expression
integerList.stream()
.sorted((a, b) -> a.compareTo(b));
//method reference
integerList.stream()
.sorted(Integer::compareTo);
So, an arbitrary object of a particular type means a reference to an instance method from some type. The program knows that it can invoke this compareTo method of Integer, so it can take the reference and any object of that type and be guaranteed the method exists.
Let us look at another example.
String[] strArray = {“John”, “Ann”, “Ellen”, “Barbara”,
“Patricia”, “Mary”, “Mike”, “Rose”};
//lambda expression
Arrays.sort(strArray, (a, b) -> a.compareToIgnoreCase(b));
//method reference
Arrays.sort(strArray, String::compareToIgnoreCase);
Here we see the same thing. The program is guaranteed that a method of this String type exists and takes parameter any object of this type.
So, here the method reference is easier to understand and read.
• Constructor
We can reference a constructor of a class in the same way that we referenced a static method. But its syntax will look like this:
ClassName::new
Let us look at this example.
We have a class Letter:
class Letter {
Letter(String let) {
System.out.print(let);
}
}
An interface with a method returning type of the class above:
interface Writing {
Letter getLetter(String let);
}
And here we can see the usage of the Constructor reference:
public class ClassA {
public static void main(String[] args) {
//lambda expression
Writing hello = (a) -> new Letter(a);
hello.getLetter(“Hello”);
//method reference
Writing hello1 = Letter::new;
hello1.getLetter(“Hello1”);
}
}
So, in conclusion the Method References are just used for referencing a method in their call. Using them, your code will be more concise and easy to read.
Leave a Reply