I was exploring the Java 8 source and found this particular part of code very surprising:
//defined in IntPipeline.java
@Override
public fin
::
is called Method Reference. It is basically a reference to a single method. I.e. it refers to an existing method by name.
Short Explanation:
Below is an example of a reference to a static method:
class Hey {
public static double square(double num){
return Math.pow(num, 2);
}
}
Function square = Hey::square;
double ans = square.apply(23d);
square
can be passed around just like object references and triggered when needed. In fact, it can be just as easily used as a reference to "normal" methods of objects as static
ones. For example:
class Hey {
public double square(double num) {
return Math.pow(num, 2);
}
}
Hey hey = new Hey();
Function square = hey::square;
double ans = square.apply(23d);
Function
above is a functional interface. To fully understand ::
, it is important to understand functional interfaces as well. Plainly, a functional interface is an interface with just one abstract method.
Examples of functional interfaces include Runnable
, Callable
, and ActionListener
.
Function
above is a functional interface with just one method: apply
. It takes one argument and produces a result.
The reason why ::
s are awesome is that:
Method references are expressions which have the same treatment as lambda expressions (...), but instead of providing a method body, they refer an existing method by name.
E.g. instead of writing the lambda body
Function square = (Double x) -> x * x;
You can simply do
Function square = Hey::square;
At runtime, these two square
methods behave exactly the same as each other. The bytecode may or may not be the same (though, for the above case, the same bytecode is generated; compile the above and check with javap -c
).
The only major criterion to satisfy is: the method you provide should have a similar signature to the method of the functional interface you use as object reference.
The below is illegal:
Supplier p = Hey::square; // illegal
square
expects an argument and returns a double
. The get
method in Supplier returns a value but does not take an argument. Thus, this results in an error.
A method reference refers to the method of a functional interface. (As mentioned, functional interfaces can have only one method each).
Some more examples: the accept
method in Consumer takes an input but doesn't return anything.
Consumer b1 = System::exit; // void exit(int status)
Consumer b2 = Arrays::sort; // void sort(Object[] a)
Consumer b3 = MyProgram::main; // void main(String... args)
class Hey {
public double getRandom() {
return Math.random();
}
}
Callable call = hey::getRandom;
Supplier call2 = hey::getRandom;
DoubleSupplier sup = hey::getRandom;
// Supplier is functional interface that takes no argument and gives a result
Above, getRandom
takes no argument and returns a double
. So any functional interface that satisfies the criteria of: take no argument and return double
can be used.
Another example:
Set set = new HashSet<>();
set.addAll(Arrays.asList("leo","bale","hanks"));
Predicate pred = set::contains;
boolean exists = pred.test("leo");
In case of parameterized types:
class Param {
T elem;
public T get() {
return elem;
}
public void set(T elem) {
this.elem = elem;
}
public static E returnSame(E elem) {
return elem;
}
}
Supplier> obj = Param::new;
Param param = obj.get();
Consumer c = param::set;
Supplier s = param::get;
Function func = Param::returnSame;
Method references can have different styles, but fundamentally they all mean the same thing and can simply be visualized as lambdas:
ClassName::methName
)instanceRef::methName
)super::methName
)ClassName::methName
)ClassName::new
)TypeName[]::new
)For further reference, see http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-final.html.