In Java 8, the double colon (::) operator is called method references. Refer to the following examples:
Anonymous class to print a list.
List<String> list = Arrays.asList("node", "java", "python", "ruby");
list.forEach(new Consumer<String>() { // anonymous class
@Override
public void accept(String str) {
System.out.println(str);
}
});
Anonymous class -> Lambda expressions.
List<String> list = Arrays.asList("node", "java", "python", "ruby");
list.forEach(str -> System.out.println(str)); // lambda
Lambda expressions -> Method references.
List<String> list = Arrays.asList("node", "java", "python", "ruby");
list.forEach(System.out::println); // method references
Anonymous Class -> Lambda expression -> Method Reference
Note
Both lambda expression or method reference does nothing but just another way call to an existing method. With method reference, it gains better readability.
There are four kinds of method references:
- Reference to a static method
ClassName::staticMethodName
- Reference to an instance method of a particular object
Object::instanceMethodName
- Reference to an instance method of an arbitrary object of a particular type
ContainingType::methodName
– - Reference to a constructor
ClassName::new
1. Static method
Lambda expression.
(args) -> ClassName.staticMethodName(args)
Method Reference.
ClassName::staticMethodName
1.1 This example prints a list of Strings, method reference to a static method SimplePrinter::print
.
Java8MethodReference1a.java
package com.favtuts.java8; import java.util.Arrays; import java.util.List; import java.util.function.Consumer; public class Java8MethodReference1a { public static void main(String[] args) { List<String> list = Arrays.asList("A", "B", "C"); // anonymous class list.forEach(new Consumer<String>() { @Override public void accept(String x) { SimplePrinter.print(x); } }); // lambda expression list.forEach(x -> SimplePrinter.print(x)); // method reference list.forEach(SimplePrinter::print); } } class SimplePrinter { public static void print(String str) { System.out.println(str); } }
1.2 This example converts a list of Strings into a list of Integers, method reference to a static method Integer::parseInt
.
Integer.java
public static int parseInt(String s) throws NumberFormatException {
return parseInt(s,10);
}
Java8MethodReference1b.java
package com.favtuts.java8; import java.util.Arrays; import java.util.List; import java.util.function.Function; import java.util.stream.Collectors; public class Java8MethodReference1b { public static void main(String[] args) { List<String> list = Arrays.asList("1", "2", "3"); // anonymous class List<Integer> collect1 = list.stream() .map(new Function<String, Integer>() { @Override public Integer apply(String s) { return Integer.parseInt(s); } }) .collect(Collectors.toList()); // lambda expression List<Integer> collect2 = list.stream() .map(s -> Integer.parseInt(s)) .collect(Collectors.toList()); // method reference List<Integer> collect3 = list.stream() .map(Integer::parseInt) .collect(Collectors.toList()); } }
1.3 This example joins two Integer
and returns a String
. It passes a method reference static method IntegerUtils::join
as an argument into another method that accepts a BiFunction
.
Java8MethodReference1c.java
package com.favtuts.java8; import java.util.function.BiFunction; public class Java8MethodReference1c { public static void main(String[] args) { // anonymous class String result1 = playTwoArgument(1, 2, new BiFunction<Integer, Integer, String>() { @Override public String apply(Integer a, Integer b) { return IntegerUtils.join(a, b); } }); // 3 // lambda String result2 = playTwoArgument(1, 2, (a, b) -> IntegerUtils.join(a, b)); // 3 // method reference String result3 = playTwoArgument(1, 2, IntegerUtils::join); // 3 } private static <R> R playTwoArgument(Integer i1, Integer i2, BiFunction<Integer, Integer, R> func) { return func.apply(i1, i2); } } class IntegerUtils{ public static String join(Integer a, Integer b) { return String.valueOf(a + b); } }
2. Reference to an instance method of a particular object
Lambda expression.
(args) -> object.instanceMethodName(args)
Method Reference.
object::instanceMethodName
2.1 This example sorts a list of Employee
by salary. We can reference to an instance method compareBySalary
of a particular object ComparatorProvider
.
Java8MethodReference2.java
package com.favtuts.java8; import java.math.BigDecimal; import java.util.Arrays; import java.util.List; public class Java8MethodReference2 { public static void main(String[] args) { List<Employee> list = Arrays.asList( new Employee("favtuts", 38, BigDecimal.valueOf(3800)), new Employee("zilap", 5, BigDecimal.valueOf(100)), new Employee("ali", 25, BigDecimal.valueOf(2500)), new Employee("unknown", 99, BigDecimal.valueOf(9999))); // anonymous class /*list.sort(new Comparator<Employee>() { @Override public int compare(Employee o1, Employee o2) { return provider.compareBySalary(o1, o2); } });*/ ComparatorProvider provider = new ComparatorProvider(); // lambda // list.sort((o1, o2) -> provider.compareBySalary(o1, o2)); // method reference list.sort(provider::compareBySalary); list.forEach(x -> System.out.println(x)); } } class ComparatorProvider { public int compareByAge(Employee o1, Employee o2) { return o1.getAge().compareTo(o2.getAge()); } public int compareByName(Employee o1, Employee o2) { return o1.getName().compareTo(o2.getName()); } public int compareBySalary(Employee o1, Employee o2) { return o1.getAge().compareTo(o2.getAge()); } }
Employee.java
package com.favtuts.java8; import java.math.BigDecimal; public class Employee { String name; Integer age; BigDecimal salary; // generated by IDE, getters, setters, constructor, toString public Employee(String name, Integer age, BigDecimal salary) { this.name = name; this.age = age; this.salary = salary; } @Override public String toString() { return "Employee{" + " name='" + getName() + "'" + ", age=" + getAge() + ", salary=" + getSalary() + "}"; } public String getName() { return this.name; } public void setName(String name) { this.name = name; } public Integer getAge() { return this.age; } public void setAge(Integer age) { this.age = age; } public BigDecimal getSalary() { return this.salary; } public void setSalary(BigDecimal salary) { this.salary = salary; } }
Output
Employee{name='zilap', age=5, salary=100}
Employee{name='ali', age=25, salary=2500}
Employee{name='favtuts', age=38, salary=3800}
Employee{name='unknown', age=99, salary=9999}
3. Reference to an instance method of an arbitrary object of a particular type.
The statement is a bit confusing, need little explanation, see below samples:
Lambda expression.
// arg0 is the first argument
(arg0, rest_of_args) -> arg0.methodName(rest_of_args)
// example, assume a and b are String
(a, b) -> a.compareToIgnoreCase(b)
Method Reference.
// first argument type
arg0_Type::methodName
// arg0 is type of ClassName
ClassName::methodName
// example, a is type of String
String::compareToIgnoreCase
For (String a, String b)
, where a
and b
are arbitrary names, and String
is its arbitrary type. This example uses method reference to an instance method compareToIgnoreCase
of an arbitrary object a
(first argument) of a particular type String
.
3.1 Review the official example in this Method References
String[] stringArray = { "Barbara", "James", "Mary", "John",
"Patricia", "Robert", "Michael", "Linda" };
Arrays.sort(stringArray, String::compareToIgnoreCase);
We passed a method reference String::compareToIgnoreCase
as a comparator for Arrays.sort
.
explanation
Review the Arrays.sort
method signature:
public static <T> void sort(T[] a, Comparator<? super T> c) {
}
In the above example, Arrays.sort
expects a Comparator<String>
. The Comparator
is a function interface, its abstract method compare
matches BiFunction<String, String, Integer>
, it takes two arguments of String
and returns an int
.
Comparator.java
@FunctionalInterface
public interface Comparator<T> {
int compare(T o1, T o2); // this matches BiFunction<String, String, Integer>
}
Review the BiFunction
method signature:
BiFunction.java
@FunctionalInterface
public interface BiFunction<T, U, R> {
R apply(T t, U u);
}
Further Reading –Java 8 BiFunction examples
The below lambda provides implementation for BiFunction<String,String,Integer>
, so the Arrays.sort
accepts the below lambda expression as valid syntax.
(String a, String b) -> a.compareToIgnoreCase(b) // return int
// a is type of String
// method reference
String::compareToIgnoreCase
3.2 Let us see another example.
Java8MethodReference3a.java
package com.favtuts.java8; import java.util.function.BiPredicate; import java.util.function.Function; public class Java8MethodReference3a { public static void main(String[] args) { // lambda int result = playOneArgument("favtuts", x -> x.length()); // 7 // method reference int result2 = playOneArgument("favtuts", String::length); // 7 // lambda Boolean result3 = playTwoArgument("favtuts", "f", (a, b) -> a.contains(b)); // true // method reference Boolean result4 = playTwoArgument("favtuts", "f", String::contains); // true // lambda Boolean result5 = playTwoArgument("favtuts", "1", (a, b) -> a.startsWith(b)); // false // method reference Boolean result6 = playTwoArgument("favtuts", "y", String::startsWith); // false System.out.println(result6); } static <R> R playOneArgument(String s1, Function<String, R> func) { return func.apply(s1); } static Boolean playTwoArgument(String s1, String s2, BiPredicate<String, String> func) { return func.test(s1, s2); } }
3.3 Let us see another example, custom object.
Java8MethodReference3b.java
package com.favtuts.java; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.function.BiFunction; public class Java8MethodReference3b { public static void main(String[] args) { Invoice obj = new Invoice("A001", BigDecimal.valueOf(1.99), 3); InvoiceCalculator formula = new InvoiceCalculator(); // lambda BigDecimal result = calculate(formula, obj, (f, o) -> f.normal(o)); // 5.97 // method reference BigDecimal result2 = calculate(formula, obj, InvoiceCalculator::normal); // 5.97 // lambda BigDecimal result3 = calculate(formula, obj, (f, o) -> f.promotion(o)); // 5.37 // method reference BigDecimal result4 = calculate(formula, obj, InvoiceCalculator::promotion); // 5.37 } static BigDecimal calculate(InvoiceCalculator formula, Invoice s1, BiFunction<InvoiceCalculator, Invoice, BigDecimal> func) { return func.apply(formula, s1); } } class InvoiceCalculator { public BigDecimal normal(Invoice obj) { return obj.getUnitPrice().multiply(BigDecimal.valueOf(obj.qty)); } public BigDecimal promotion(Invoice obj) { return obj.getUnitPrice() .multiply(BigDecimal.valueOf(obj.qty)) .multiply(BigDecimal.valueOf(0.9)) .setScale(2, RoundingMode.HALF_UP); } } class Invoice { String no; BigDecimal unitPrice; Integer qty; // generated by IDE, setters, gettes, constructor, toString }
The first argument is a type of InvoiceCalculator
. So, we can reference to an instance method (normal or promotion
) of an arbitrary object (f
) of a particular type InvoiceCalculator
.
(f, o) -> f.normal(o))
(f, o) -> f.promotion(o))
InvoiceCalculator::normal
InvoiceCalculator::promotion
Got it? No more example 🙂
4. Reference to a constructor.
Lambda expression.
(args) -> new ClassName(args)
Method Reference.
ClassName::new
4.1 Reference to a default constructor.
Java8MethodReference4a.java
package com.favtuts.java8; import java.math.BigDecimal; import java.util.HashMap; import java.util.Map; import java.util.function.Supplier; public class Java8MethodReference4a { public static void main(String[] args) { // lambda Supplier<Map> obj1 = () -> new HashMap(); // default HashMap() constructor Map map1 = obj1.get(); // method reference Supplier<Map> obj2 = HashMap::new; Map map2 = obj2.get(); // lambda Supplier<Invoice> obj3 = () -> new Invoice(); // default Invoice() constructor Invoice invoice1 = obj3.get(); // method reference Supplier<Invoice> obj4 = Invoice::new; Invoice invoice2 = obj4.get(); } } class Invoice { String no; BigDecimal unitPrice; Integer qty; public Invoice() { } //... generated by IDE }
4.2 Reference to a constructor which accepts an argument – Invoice(BigDecimal unitPrice)
Java8MethodReference4b.java
package com.favtuts.java8; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.function.Function; public class Java8MethodReference4b { public static void main(String[] args) { List<BigDecimal> list = Arrays.asList( BigDecimal.valueOf(9.99), BigDecimal.valueOf(2.99), BigDecimal.valueOf(8.99)); // lambda // List<Invoice> invoices = fakeInvoice(list, (price) -> new Invoice(price)); // method reference List<Invoice> invoices = fakeInvoice(list, Invoice::new); invoices.forEach(System.out::println); } static List<Invoice> fakeInvoice(List<BigDecimal> list, Function<BigDecimal, Invoice> func) { List<Invoice> result = new ArrayList<>(); for (BigDecimal amount : list) { result.add(func.apply(amount)); } return result; } } class Invoice { String no; BigDecimal unitPrice; Integer qty; public Invoice(BigDecimal unitPrice) { this.unitPrice = unitPrice; } //... generated by IDE }
Output
Invoice{no='null', unitPrice=9.99, qty=null}
Invoice{no='null', unitPrice=2.99, qty=null}
Invoice{no='null', unitPrice=8.99, qty=null}
Done.
Download Source Code
$ git clone https://github.com/favtuts/java-core-tutorials-examples
$ cd java-basic/java8