I was exploring the Java 8 source and found this particular part of code very surprising:
// Defined in IntPipeline.java
@Override
public final OptionalInt reduce(IntBinaryOperator op) {
return evaluate(ReduceOps.makeInt(op));
}
@Override
public final OptionalInt max() {
return reduce(Math::max); // This is the gotcha line
}
// Defined in Math.java
public static int max(int a, int b) {
return (a >= b) ? a : b;
}
Is Math::max
something like a method pointer? How does a normal static
method get converted to IntBinaryOperator
?
TestingLambda$$Lambda$2/8460669
and TestingLambda$$Lambda$3/11043253
were created on two invocations.
Usually, one would call the reduce
method using Math.max(int, int)
as follows:
reduce(new IntBinaryOperator() {
int applyAsInt(int left, int right) {
return Math.max(left, right);
}
});
That requires a lot of syntax for just calling Math.max
. That's where lambda expressions come into play. Since Java 8 it is allowed to do the same thing in a much shorter way:
reduce((int left, int right) -> Math.max(left, right));
How does this work? The java compiler "detects", that you want to implement a method that accepts two int
s and returns one int
. This is equivalent to the formal parameters of the one and only method of interface IntBinaryOperator
(the parameter of method reduce
you want to call). So the compiler does the rest for you - it just assumes you want to implement IntBinaryOperator
.
But as Math.max(int, int)
itself fulfills the formal requirements of IntBinaryOperator
, it can be used directly. Because Java 7 does not have any syntax that allows a method itself to be passed as an argument (you can only pass method results, but never method references), the ::
syntax was introduced in Java 8 to reference methods:
reduce(Math::max);
Note that this will be interpreted by the compiler, not by the JVM at runtime! Although it produces different bytecodes for all three code snippets, they are semantically equal, so the last two can be considered to be short (and probably more efficient) versions of the IntBinaryOperator
implementation above!
(See also Translation of Lambda Expressions)
::
is called a 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 it 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 it 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
doesn't take any 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:
A static method (ClassName::methName) An instance method of a particular object (instanceRef::methName) A super method of a particular object (super::methName) An instance method of an arbitrary object of a particular type (ClassName::methName) A class constructor reference (ClassName::new) An array constructor reference (TypeName[]::new)
For further reference, see State of the Lambda.
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);
}
}
method(Math::max);
is invocation and definition of method would be like public static void method(IntBinaryOperator op){System.out.println(op.applyAsInt(1, 2));}
. That's how its used.
A lambda expression is used to create anonymous methods. It does nothing but call an existing method, but it is clearer to refer to the method directly by its name. And method reference enables us to do that using method-reference operator ::
.
Consider the following simple class where each employee has a name and grade.
public class Employee {
private String name;
private String grade;
public Employee(String name, String grade) {
this.name = name;
this.grade = grade;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGrade() {
return grade;
}
public void setGrade(String grade) {
this.grade = grade;
}
}
Suppose we have a list of employees returned by some method and we want to sort the employees by their grade. We know we can make use of anonymous class as:
List<Employee> employeeList = getDummyEmployees();
// Using anonymous class
employeeList.sort(new Comparator<Employee>() {
@Override
public int compare(Employee e1, Employee e2) {
return e1.getGrade().compareTo(e2.getGrade());
}
});
where getDummyEmployee() is some method as:
private static List<Employee> getDummyEmployees() {
return Arrays.asList(new Employee("Carrie", "C"),
new Employee("Fanishwar", "F"),
new Employee("Brian", "B"),
new Employee("Donald", "D"),
new Employee("Adam", "A"),
new Employee("Evan", "E")
);
}
Now we know that Comparator is a functional interface. A functional interface is the one with exactly one abstract method (though it may contain one or more default or static methods). Lambda expression provides implementation of @FunctionalInterface
so a functional interface can have only one abstract method. We can use the lambda expression as:
employeeList.sort((e1,e2) -> e1.getGrade().compareTo(e2.getGrade())); // Lambda expression
It seems all good, but what if the class Employee
also provides a similar method?
public class Employee {
private String name;
private String grade;
// getter and setter
public static int compareByGrade(Employee e1, Employee e2) {
return e1.grade.compareTo(e2.grade);
}
}
In this case, using the method name itself will be more clear. Hence we can directly refer to the method by using the method reference as: employeeList.sort(Employee::compareByGrade); // Method reference
As per the documentation, there are four kinds of method references:
+----+-------------------------------------------------------+--------------------------------------+
| | Kind | Example |
+----+-------------------------------------------------------+--------------------------------------+
| 1 | Reference to a static method | ContainingClass::staticMethodName |
+----+-------------------------------------------------------+--------------------------------------+
| 2 |Reference to an instance method of a particular object | containingObject::instanceMethodName |
+----+-------------------------------------------------------+--------------------------------------+
| 3 | Reference to an instance method of an arbitrary object| ContainingType::methodName |
| | of a particular type | |
+----+-------------------------------------------------------+--------------------------------------+
| 4 |Reference to a constructor | ClassName::new |
+------------------------------------------------------------+--------------------------------------+
::
is a new operator included in Java 8 that is used to refer to a method of an existing class. You can refer to static methods and non-static methods of a class.
For referring to static methods, the syntax is:
ClassName :: methodName
For referring to non-static methods, the syntax is
objRef :: methodName
And
ClassName :: methodName
The only prerequisite for referring to a method is that the 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.
This was found on: http://www.speakingcs.com/2014/08/method-references-in-java-8.html
This is a method reference in Java 8. The Oracle documentation is here.
As stated in the documentation...
The method reference Person::compareByAge is a reference to a static method. The following is an example of a reference to an instance method of a particular object:
class ComparisonProvider {
public int compareByName(Person a, Person b) {
return a.getName().compareTo(b.getName());
}
public int compareByAge(Person a, Person b) {
return a.getBirthday().compareTo(b.getBirthday());
}
}
ComparisonProvider myComparisonProvider = new ComparisonProvider();
Arrays.sort(rosterAsArray, myComparisonProvider::compareByName);
The method reference myComparisonProvider::compareByName invokes the method compareByName that is part of the object myComparisonProvider. The JRE infers the method type arguments, which in this case are (Person, Person).
The :: operator was introduced in Java 8 for method references. A method reference is the shorthand syntax for a lambda expression that executes just one method. Here's the general syntax of a method reference:
Object :: methodName
We know that we can use lambda expressions instead of using an anonymous class. But sometimes, the lambda expression is really just a call to some method, for example:
Consumer<String> c = s -> System.out.println(s);
To make the code clearer, you can turn that lambda expression into a method reference:
Consumer<String> c = System.out::println;
c.accept(s);
::
is basically an even shorter lambda expression. So object::nonstaticmethod
is ()-> object.nonstaticmethod()
or event-> object.nonstaticmethod()
. And for class.method()
simply class::method
.
So I see here tons of answers that are frankly overcomplicated, and that's an understatement.
The answer is pretty simple: **:: is called a method references. In Method References 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 reference:
A::b somewhat substitutes the following inline lambda expression: (parameters ...) -> A.b(parameter ...)
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 one method).
Let's take a short look on 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)
{
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 the method reference (::). 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.
I found this source very interesting.
In fact, it is the lambda that turns into a double colon. The double colon is more readable.
We follow these steps:
Step 1
// We create a comparator of two persons
Comparator c = (Person p1, Person p2) -> p1.getAge().compareTo(p2.getAge());
Step 2
// We use the interference
Comparator c = (p1, p2) -> p1.getAge().compareTo(p2.getAge());
Step 3
// The magic using method reference
Comparator c = Comparator.comparing(Person::getAge);
Person::getAge()
should be Person::getAge
.
The :: is known as a method reference. Let’s say we want to call a calculatePrice method of class Purchase. Then we can write it as:
Purchase::calculatePrice
It can also be seen as a short form of writing the lambda expression, because method references are converted into lambda expressions.
In older Java versions, instead of "::" or lambd, you can use:
public interface Action {
void execute();
}
public class ActionImpl implements Action {
@Override
public void execute() {
System.out.println("execute with ActionImpl");
}
}
public static void main(String[] args) {
Action action = new Action() {
@Override
public void execute() {
System.out.println("execute with anonymous class");
}
};
action.execute();
//or
Action actionImpl = new ActionImpl();
actionImpl.execute();
}
Or passing to the method:
public static void doSomething(Action action) {
action.execute();
}
In Java 8, Streams Reducer in simple works as a function which takes two values as input and returns the result after some calculation. This result is fed into the next iteration.
In case of the Math:max function, the method keeps returning the maximum of two values passed and in the end you have the largest number in hand.
Since many answers here explained well ::
behaviour, additionally I would like to clarify that ::
operator doesn’t need to have exactly same signature as the referring functional interface if it is used for instance variables. Let’s assume we need a BinaryOperator which has type of TestObject. In the traditional way it’s implemented like this:
BinaryOperator<TestObject> binary = new BinaryOperator<TestObject>() {
@Override
public TestObject apply(TestObject t, TestObject u) {
return t;
}
};
As you see in the anonymous implementation, it requires two TestObject arguments and returns a TestObject object as well. To satisfy this condition by using the ::
operator, we can start with a static method:
public class TestObject {
public static final TestObject testStatic(TestObject t, TestObject t2) {
return t;
}
}
And then call:
BinaryOperator<TestObject> binary = TestObject::testStatic;
Ok, it compiled fine. What about if we need an instance method? Let’s update TestObject with an instance method:
public class TestObject {
public final TestObject testInstance(TestObject t, TestObject t2) {
return t;
}
public static final TestObject testStatic(TestObject t, TestObject t2) {
return t;
}
}
Now we can access the instance as below:
TestObject testObject = new TestObject();
BinaryOperator<TestObject> binary = testObject::testInstance;
This code compiles fine, but the below one does not:
BinaryOperator<TestObject> binary = TestObject::testInstance;
My Eclipse tells me "Cannot make a static reference to the non-static method testInstance(TestObject, TestObject) from the type TestObject ..."
Fair enough. It’s an instance method, but if we overload testInstance
as below:
public class TestObject {
public final TestObject testInstance(TestObject t) {
return t;
}
public final TestObject testInstance(TestObject t, TestObject t2) {
return t;
}
public static final TestObject testStatic(TestObject t, TestObject t2) {
return t;
}
}
And call:
BinaryOperator<TestObject> binary = TestObject::testInstance;
The code will just compile fine. Because it will call testInstance
with a single parameter instead of a double one. OK, so what happened our two parameter? Let’s print out and see:
public class TestObject {
public TestObject() {
System.out.println(this.hashCode());
}
public final TestObject testInstance(TestObject t) {
System.out.println("Test instance called. this.hashCode:" +
this.hashCode());
System.out.println("Given parameter hashCode:" + t.hashCode());
return t;
}
public final TestObject testInstance(TestObject t, TestObject t2) {
return t;
}
public static final TestObject testStatic(TestObject t, TestObject t2) {
return t;
}
}
Which will output:
1418481495
303563356
Test instance called. this.hashCode:1418481495
Given parameter hashCode:303563356
OK, so the JVM is smart enough to call param1.testInstance(param2). Can we use testInstance
from another resource but not TestObject? I.e.:
public class TestUtil {
public final TestObject testInstance(TestObject t) {
return t;
}
}
And call:
BinaryOperator<TestObject> binary = TestUtil::testInstance;
It will just not compile and the compiler will say: "The type TestUtil does not define testInstance(TestObject, TestObject)".
So the compiler will look for a static reference if it is not the same type. OK, what about polymorphism? If we remove the final modifiers and add our SubTestObject class:
public class SubTestObject extends TestObject {
public final TestObject testInstance(TestObject t) {
return t;
}
}
And call:
BinaryOperator<TestObject> binary = SubTestObject::testInstance;
It will not compile either. The compiler will still look for a static reference. But the below code will compile fine since it is passing the is-a test:
public class TestObject {
public SubTestObject testInstance(Object t) {
return (SubTestObject) t;
}
}
BinaryOperator<TestObject> binary = TestObject::testInstance;
return reduce(Math::max);
is not equal to return reduce(max());
.
But it means something like this:
IntBinaryOperator myLambda = (a, b)->{(a >= b) ? a : b};//56 keystrokes I had to type -_-
return reduce(myLambda);
You can just save 47 keystrokes if you write like this:
return reduce(Math::max); // Only 9 keystrokes ^_^
The previous answers are quite complete regarding what the ::
method reference does. To sum up, it provides a way to refer to a method (or constructor) without executing it, and when evaluated, it creates an instance of the functional interface that provides the target type context.
Below are two examples to find an object with the maximum value in an ArrayList
with and without the use of ::
method reference. Explanations are in the comments below.
Without the use of ::
import java.util.*;
class MyClass {
private int val;
MyClass (int v) { val = v; }
int getVal() { return val; }
}
class ByVal implements Comparator<MyClass> {
// no need to create this class when using method reference
public int compare(MyClass source, MyClass ref) {
return source.getVal() - ref.getVal();
}
}
public class FindMaxInCol {
public static void main(String args[]) {
ArrayList<MyClass> myClassList = new ArrayList<MyClass>();
myClassList.add(new MyClass(1));
myClassList.add(new MyClass(0));
myClassList.add(new MyClass(3));
myClassList.add(new MyClass(6));
MyClass maxValObj = Collections.max(myClassList, new ByVal());
}
}
With the use of ::
import java.util.*;
class MyClass {
private int val;
MyClass (int v) { val = v; }
int getVal() { return val; }
}
public class FindMaxInCol {
static int compareMyClass(MyClass source, MyClass ref) {
// This static method is compatible with the compare() method defined by Comparator.
// So there's no need to explicitly implement and create an instance of Comparator like the first example.
return source.getVal() - ref.getVal();
}
public static void main(String args[]) {
ArrayList<MyClass> myClassList = new ArrayList<MyClass>();
myClassList.add(new MyClass(1));
myClassList.add(new MyClass(0));
myClassList.add(new MyClass(3));
myClassList.add(new MyClass(6));
MyClass maxValObj = Collections.max(myClassList, FindMaxInCol::compareMyClass);
}
}
At runtime they behave exactly the same. The bytecode may not be the same (for the above in case, it generates the same bytecode (compile above and check javaap -c;)).
At runtime they behave exactly the same. The method (math::max) it generates the same math (compile the above and check javap -c;)).
javaap -c;)
can't be right. Can you elaborate?
The double colon, i.e., the ::
operator, was introduced in Java 8 as a method reference. A method reference is a form of lambda expression which is used to reference the existing method by its name.
classname::methodName
Example:
stream.forEach(element -> System.out.println(element))
By using double colon ::
stream.forEach(System.out::println(element))
Success story sharing