I usually post about Java stuff on Twitter - you should follow me there:
Follow @baeldung1. 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.
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 a number of 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 parameter in 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 it can be imported and used as-is.