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