:: (double colon) operator in Java 8

前端 未结 17 2821
旧时难觅i
旧时难觅i 2020-11-21 11:10

I was exploring the Java 8 source and found this particular part of code very surprising:

//defined in IntPipeline.java
@Override
public fin         


        
17条回答
  •  甜味超标
    2020-11-21 11:41

    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

提交回复
热议问题