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<Double, Double> 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<Double, Double> 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<Double, Double> square = (Double x) -> x * x;
You can simply do
Function<Double, Double> 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<Boolean> 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<Integer> b1 = System::exit; // void exit(int status)
Consumer<String[]> b2 = Arrays::sort; // void sort(Object[] a)
Consumer<String> b3 = MyProgram::main; // void main(String... args)
class Hey {
public double getRandom() {
return Math.random();
}
}
Callable<Double> call = hey::getRandom;
Supplier<Double> 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<String> set = new HashSet<>();
set.addAll(Arrays.asList("leo","bale","hanks"));
Predicate<String> pred = set::contains;
boolean exists = pred.test("leo");
In case of parameterized types:
class Param<T> {
T elem;
public T get() {
return elem;
}
public void set(T elem) {
this.elem = elem;
}
public static <E> E returnSame(E elem) {
return elem;
}
}
Supplier<Param<Integer>> obj = Param<Integer>::new;
Param<Integer> param = obj.get();
Consumer<Integer> c = param::set;
Supplier<Integer> s = param::get;
Function<String, String> func = Param::<String>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.
Yes, that is true. The ::
operator is used for method referencing. So, one can extract static methods from classes by using it or methods from objects. The same operator can be used even for constructors. All cases mentioned here are exemplified in the code sample below.
The official documentation from Oracle can be found here.
You can have a better overview of the JDK 8 changes in this article. In the Method/Constructor referencing section a code example is also provided:
interface ConstructorReference {
T constructor();
}
interface MethodReference {
void anotherMethod(String input);
}
public class ConstructorClass {
String value;
public ConstructorClass() {
value = "default";
}
public static void method(String input) {
System.out.println(input);
}
public void nextMethod(String input) {
// operations
}
public static void main(String... args) {
// constructor reference
ConstructorReference reference = ConstructorClass::new;
ConstructorClass cc = reference.constructor();
// static method reference
MethodReference mr = cc::method;
// object method reference
MethodReference mr2 = cc::nextMethod;
System.out.println(cc.value);
}
}
So I see here tons of answers that are frankly overcomplicated, and that's an understatement.
The answer is pretty simple: :: it's called a Method References https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html
So I won't copy-paste, on the link, you can find all the information if you scroll down to the table.
Now, let's take a short look at what is a Method References:
A::B somewhat substitutes the following inline lambda expression: (params ...) -> A.B(params ...)
To correlate this with your questions, it's necessary to understand a java lambda expression. Which is not hard.
An inline lambda expression is similar to a defined functional interface (which is an interface that has no more and no less than 1 method). Let's take a short look what I mean:
InterfaceX f = (x) -> x*x;
InterfaceX must be a functional interface. Any functional interface, the only thing what's important about InterfaceX for that compiler is that you define the format:
InterfaceX can be any of this:
interface InterfaceX
{
public Integer callMe(Integer x);
}
or this
interface InterfaceX
{
public Double callMe(Integer x);
}
or more generic:
interface InterfaceX<T,U>
{
public T callMe(U x);
}
Let's take the first presented case and the inline lambda expression that we defined earlier.
Before Java 8, you could've defined it similarly this way:
InterfaceX o = new InterfaceX(){
public int callMe (int x, int y)
{
return x*x;
} };
Functionally, it's the same thing. The difference is more in how the compiler perceives this.
Now that we took a look at inline lambda expression, let's return to Method References (::). Let's say you have a class like this:
class Q {
public static int anyFunction(int x)
{
return x+5;
}
}
Since method anyFunctions has the same types as InterfaceX callMe, we can equivalate those two with a Method Reference.
We can write it like this:
InterfaceX o = Q::anyFunction;
and that is equivalent to this :
InterfaceX o = (x) -> Q.anyFunction(x);
A cool thing and advantage of Method References are that at first, until you assign them to variables, they are typeless. So you can pass them as parameters to any equivalent looking (has same defined types) functional interface. Which is exactly what happens in your case
Double colon i.e. ::
operator is introduced in Java 8 as a method reference. Method reference is a form of lambda expression which is used to refer the existing method by its name.
classname::methodName
ex:-
stream.forEach(element -> System.out.println(element))
By using Double Colon ::
stream.forEach(System.out::println(element))
::
is a new operator included in Java 8 that is used to refer a method of an existing class. You can refer static methods and non-static methods of a class.
For referring static methods, the syntax is:
ClassName :: methodName
For referring non-static methods, the syntax is
objRef :: methodName
And
ClassName :: methodName
The only prerequisite for referring a method is that method exists in a functional interface, which must be compatible with the method reference.
Method references, when evaluated, create an instance of the functional interface.
Found on: http://www.speakingcs.com/2014/08/method-references-in-java-8.html