I was exploring the Java 8 source and found this particular part of code very surprising:
//defined in IntPipeline.java
@Override
public fin
Since many answers here explained well ::
behaviour, additionally I would like to clarify that ::
operator doesnt need to have exactly same signature as the referring Functional Interface if it is used for instance variables. Lets assume we need a BinaryOperator which has type of TestObject. In traditional way its implemented like this:
BinaryOperator binary = new BinaryOperator() {
@Override
public TestObject apply(TestObject t, TestObject u) {
return t;
}
};
As you see in anonymous implementation it requires two TestObject argument and returns a TestObject object as well. To satisfy this condition by using ::
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 binary = TestObject::testStatic;
Ok it compiled fine. What about if we need an instance method? Lets update TestObject with 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 instance as below:
TestObject testObject = new TestObject();
BinaryOperator binary = testObject::testInstance;
This code compiles fine, but below one not:
BinaryOperator binary = TestObject::testInstance;
My eclipse tell me "Cannot make a static reference to the non-static method testInstance(TestObject, TestObject) from the type TestObject ..."
Fair enough its 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 binary = TestObject::testInstance;
The code will just compile fine. Because it will call testInstance
with single parameter instead of double one. Ok so what happened our two parameter? Lets printout 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 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 binary = TestUtil::testInstance;
It will just not compile and compiler will tell: "The type TestUtil does not define testInstance(TestObject, TestObject)". So compiler will look for a static reference if it is not the same type. Ok what about polymorphism? If we remove final modifiers and add our SubTestObject class:
public class SubTestObject extends TestObject {
public final TestObject testInstance(TestObject t){
return t;
}
}
And call:
BinaryOperator binary = SubTestObject::testInstance;
It will not compile as well, compiler will still look for static reference. But below code will compile fine since it is passing is-a test:
public class TestObject {
public SubTestObject testInstance(Object t){
return (SubTestObject) t;
}
}
BinaryOperator binary = TestObject::testInstance;
*I am just studying so I have figured out by try and see, feel free to correct me if I am wrong