At IDR Solutions we use Java 8 for the development of our products (a Java PDF Viewer and SDK, PDF to HTML5 converter and a Java ImageIO replacement). As I spend a lot of time using Java 8 I thought that it might be useful to a series article of the new features in JDK8.
In this article, we will be looking at Method References.
What are Method References?
It is a feature that is related to Lambda Expression. It allows us to reference constructors or methods without executing them. Method references and Lambda are similar in that they both require a target type that consists of a compatible functional interface.
Types of Method Reference
There are four types of method reference, the table below summarizes this.
Type | Example | Syntax |
1. Reference to a static method | ContainingClass::staticMethodName | Class::staticMethodName |
2. Reference to a constructor | ClassName::new | ClassName::new |
3. Reference to an instance method of an arbitrary object of a particular type | ContainingType::methodName | Class::instanceMethodName |
4. Reference to an instance method of a particular object | containingObject::instanceMethodName | object::instanceMethodName |
I will explain further the four types of methods referenced in the table.
1 . Reference to a Static Method
public class ReferenceToStaticMethodExample {
/**
* @param args the command line arguments
*/ public static void main(String[] args) {
List numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16);
List primeNumbers = ReferenceToStaticMethodExample.findPrimeNumbers(numbers,
(number) -> ReferenceToStaticMethodExample.isPrime((int) number));
System.out.println("Prime Numbers are " + primeNumbers);
}
public static boolean isPrime(int number) {
if (number == 1) {
return false;
}
for (int i = 2; i < number; i++) { if (number % i == 0) { return false; } } return true; } public static List findPrimeNumbers(List list, Predicate predicate) { List sortedNumbers = new ArrayList(); list.stream().filter((i) -> (predicate.test(i))).forEach((i) -> {
sortedNumbers.add(i);
});
return sortedNumbers;
}
}
As you can see in this code, we made reference to a static method in this class.
ContainingClass::staticMethodName
Containing class | ReferenceToStaticMethod |
staticMethodName | isPrime |
As I mentioned above, the Method reference is very similar to Lambda. Let’s look at the difference here
Lambda Form | List primeNumbers = ReferenceToStaticMethod.testPredicate(numbers, a -> ReferenceToStaticMethod.isPrime(a)); |
Method Reference | List primeNumbers = ReferenceToStaticMethod.testPredicate(numbers, ReferenceToStaticMethod::isPrime); |
2. Reference To Constructor
public class ReferenceToConstructor {
/**
* @param args the command line arguments
*/ public static void main(String[] args) {
// TODO code application logic here
List numbers = Arrays.asList(4,9,16,25,36);
List squaredNumbers = ReferenceToConstructor.findSquareRoot(numbers,Integer::new);
System.out.println("Square root of numbers = "+squaredNumbers);
}
private static List findSquareRoot(List list, Function<Integer,Integer> f){
List result = new ArrayList();
list.forEach(x -> result.add(Math.sqrt(f.apply((Integer) x))));
return result;
}
}
This is very similar to a reference to a static method. The difference between the two is, the constructor reference method name is new
.
ClassName::new
ClassName | Integer |
new | new |
Lambda Form | List squaredNumbers = ReferenceToConstructor.findSquareRoot(numbers, x -> new Integer(x)); |
Method Reference | List squaredNumbers= ReferenceToConstructor.findSquareRoot(numbers,Integer::new); |
3. Reference To an Instance Method Of An Arbitrary Object Of A Particular Type
public class ReferenceToInstanceMethodAOPT {
/**
* @param args the command line arguments
*/
private static class Person {
private final String name;
private final int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
public static void main(String[] args) {
// TODO code application logic here
List persons = new ArrayList();
persons.add(new Person("Albert", 80));
persons.add(new Person("Ben", 15));
persons.add(new Person("Charlote", 20));
persons.add(new Person("Dean", 6));
persons.add(new Person("Elaine", 17));
List allAges = ReferenceToInstanceMethodAOPT.listAllAges(persons, Person::getAge);
System.out.println("Printing out all ages \n"+allAges);
}
private static List listAllAges(List person, Function<Person, Integer> f){
List result = new ArrayList();
person.forEach(x -> result.add(f.apply((Person)x)));
return result;
}
}
This mean providing reference to any of the persons
object in the List of a particular type which is the Person
.So the containing type is persons
and the method name is getAge();
ContainingType::methodName
ContainingType | Person |
methodName | getAge |
Lambda Form | List allAges = ReferenceToInstanceMethodAOPT.listAllAges(persons, x -> x.getAge()); |
Method Reference | List allAges = ReferenceToInstanceMethodAOPT.listAllAges(persons, Person::getAge); |
4. Reference To An Instance Method Of A Particular Object
public class ReferenceToInstanceMethodOAPO {
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
List names = new ArrayList();
names.add("David");
names.add("Richard");
names.add("Samuel");
names.add("Rose");
names.add("John");
ReferenceToInstanceMethodOAPO.printNames(names,System.out::println);
}
private static void printNames(List list, Consumer c ){
list.forEach(x -> c.accept(x));
}
}
Since System.out
is an instance of type PrintStream
, we then call theprintln
method of the instance.
containingObject::instanceMethodName
containingObject | System.out |
instanceMethodName | println |
Lambda Form | ReferenceToInstanceMethodOAPO.printNames(names, x -> System.out.println(x)); |
Method Reference | ReferenceToInstanceMethodOAPO.printNames(names,System.out::println); |
So what can we Take Away?
- You can use replace Lambda Expressions with Method References where Lamdba is invoking already defined methods.
- You can’t pass arguments to methods Reference
- To use Lambda and Method Reference, make sure you have Java 8 installed. They do not work on Java 7 and earlier versions.
If you found this article useful, you may also be interested in our other Java8 posts on Lambda Expression, Streams API, Default Methods, Consumer Suppliers, Optional and Repeating Annotations.
We also now have a large collection of articles on what is new in other versions of Java, including Java9, and Java13.
Our software libraries allow you to
Convert PDF to HTML in Java |
Convert PDF Forms to HTML5 in Java |
Convert PDF Documents to an image in Java |
Work with PDF Documents in Java |
Read and Write AVIF, HEIC, WEBP and other image formats |
For the first example 1 . Reference to a Static Method
specifying Generic type like `List` would make IDE happier, and then stream usage maybe simplified
public static List findPrimeNumbers(List list, Predicate predicate) {
List sortedNumbers = new ArrayList();
list.stream().filter(predicate).forEach( sortedNumbers::add );
return sortedNumbers;
}
OK, the formated code is
public static List findPrimeNumbers(List list, Predicate predicate) {
List sortedNumbers = new ArrayList();
list.stream().filter(predicate).forEach( sortedNumbers::add );
return sortedNumbers;
}
Hi,
I make some error when make the first example 1 . Reference to a Static Method
Exception in thread “main” java.lang.Error: Unresolved compilation problems:
The method findPrimeNumbers(List, Predicate) in the type ReferenceToStaticMethod is not applicable for the arguments (List, ReferenceToStaticMethod::isPrime)
The type ReferenceToStaticMethod does not define isPrime(Object) that is applicable here
at jv8.ReferenceToStaticMethod.main(ReferenceToStaticMethod.java:17)
Please help me check it.
Thank
Hi,
Thank you for pointing this out to us, I have taken a look at the example and have now altered it so that it should now work. I hope this helps.
public class ReferenceToStaticMethodExample {
/**
* @param args
* the command line arguments
*/
public static void main(String[] args) {
List numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 15, 16);
List primeNumbers = ReferenceToStaticMethodExample
.findPrimeNumbers(numbers,
ReferenceToStaticMethodExample::isPrime);
System.out.println(“Prime Numbers are ” + primeNumbers);
}
public static boolean isPrime(int number) {
if (number == 1) {
return false;
}
for (int i = 2; i < number; i++) {
if (number % i == 0) {
return false;
}
}
return true;
}
public static List findPrimeNumbers(List list,
Predicate predicate) {
List sortedNumbers = new ArrayList();
list.stream().filter(predicate).forEach(sortedNumbers::add);
return sortedNumbers;
}
}
Thank you so much for this awesome post!
Example 1 is not valid since amended to *Work but can’t use method reference because
the list carry Object not int
This is correct and the example 1 given above is not valid and should be updated.
Working code for example 2
private static List findSquareRoot(List list, Function f){
List result = new ArrayList();
list.forEach(x -> result.add(Math.sqrt(f.apply((Integer) x))));
return result;
}
“Function f ” can u explain this in detail. Do we need to separate ‘Functionf’. what it tells.
Hi Prasath,
Function is a functional interface. It can be used as the assignment target for a lambda expression or method reference. E.g. in our second example it is used to create a new Integer object. I hope that helps.
Hi in the 3rd example the function listAllAges has the following parameter
Function f)
is the integer=””- a typo ? if not can you explain what it means.
thanks !
Hi, yes it was a typo, thanks for pointing it out. I’ve edited the code now so it should work
Thanks, nice post