1. Overview
In this quick article, we'll discuss the double colon operator ( :: ) in Java 8 and go over the scenarios where the operator can be used.
Further reading:
Java 8 Interview Questions(+ Answers)
New Features in Java 8
2. From Lambdas to Double Colon Operator
With Lambdas expressions, we've seen that code can become very concise.
For example, to create a comparator, the following syntax is enough:
Comparator c = (Computer c1, Computer c2) -> c1.getAge().compareTo(c2.getAge());
Then, with type inference:
Comparator c = (c1, c2) -> c1.getAge().compareTo(c2.getAge());
But can we make the code above even more expressive and readable? Let's have a look:
Comparator c = Comparator.comparing(Computer::getAge);
We've used the :: operator as shorthand for lambdas calling a specific method – by name. And the end, result is of course even more readable syntax.
3. How Does It Work?
Very simply put, when we are using a method reference – the target reference is placed before the delimiter :: and the name of the method is provided after it.
For example:
Computer::getAge;
We're looking at a method reference to the method getAge defined in the Computer class.
We can then operate with that function:
Function<Computer, Integer> getAge = Computer::getAge;
Integer computerAge = getAge.apply(c1);
Notice that we're referencing the function – and then applying it to the right kind of argument.
4. Method References
We can make good use of this operator in quite some scenarios.
4.1. A Static Method
First, we're going to make use of a static utility method:
List inventory = Arrays.asList(
new Computer( 2015, "white", 35), new Computer(2009, "black", 65));
inventory.forEach(ComputerUtils::repair);
4.2. An Instance Method of an Existing Object
Next, let's have a look at an interesting scenario – referencing a method of an existing object instance.
We're going to use the variable System.out – an object of type PrintStream which supports the print method:
Computer c1 = new Computer(2015, "white");
Computer c2 = new Computer(2009, "black");
Computer c3 = new Computer(2014, "black");
Arrays.asList(c1, c2, c3).forEach(System.out::print);
4.3. An Instance Method of an Arbitrary Object of a Particular Type
Computer c1 = new Computer(2015, "white", 100);
Computer c2 = new MacbookPro(2009, "black", 100);
List inventory = Arrays.asList(c1, c2);
inventory.forEach(Computer::turnOnPc);
As you can see, we're referencing the turnOnPc method not on a specific instance, but on the type itself.
At line 4 the instance method turnOnPc will be called for every object of inventory.
And this naturally means that – for c1 the method turnOnPc will be called on the Computer instance and for c2 on MacbookPro instance.
4.4. A Super Method of a Particular Object
Suppose you have the following method in the Computer superclass:
public Double calculateValue(Double initialValue) {
return initialValue/1.50;
}
and this one in MacbookPro subclass:
@Override
public Double calculateValue(Double initialValue){
Function<Double, Double> function = super::calculateValue;
Double pcValue = function.apply(initialValue);
return pcValue + (initialValue/10) ;
}
A call to calculateValue method on a MacbookPro instance:
macbookPro.calculateValue(999.99);
will also produce also a call to calculateValue on the Computer superclass.
5. Constructor References
5.1. Create a New Instance
Referencing a constructor to instantiate an object can be quite simple:
@FunctionalInterface
public interface InterfaceComputer {
Computer create();
}
InterfaceComputer c = Computer::new;
Computer computer = c.create();
What if you have two parameters in a constructor?
BiFunction<Integer, String, Computer> c4Function = Computer::new;
Computer c4 = c4Function.apply(2013, "white");
If parameters are three or more you have to define a new Functional interface:
@FunctionalInterface
interface TriFunction<A, B, C, R> {
R apply(A a, B b, C c);
default <V> TriFunction<A, B, C, V> andThen( Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (A a, B b, C c) -> after.apply(apply(a, b, c));
}
}
Then, initialize your object:
TriFunction <Integer, String, Integer, Computer> c6Function = Computer::new;
Computer c3 = c6Function.apply(2008, "black", 90);
5.2. Create an Array
Finally, let's see how to create an array of Computer objects with five elements:
Function <Integer, Computer[]> computerCreator = Computer[]::new;
Computer[] computerArray = computerCreator.apply(5);
6. Conclusion
As we're starting to see, the double colon operator – introduced in Java 8 – will be very useful in some scenarios, and especially in conjunction with Streams.
It's also quite important to have a look at functional interfaces for a better understanding of what happens behind the scenes.
The complete source code for the example is available in this GitHub project – this is a Maven and Eclipse project so that it can be imported and used as-is.