由于这里的许多答案都很好地解释了::行为,另外,我想澄清::操作符不需要与引用函数接口具有完全相同的签名,如果它用于实例变量。让我们假设我们需要一个类型为TestObject的BinaryOperator。传统的实现方式是这样的:
BinaryOperator<TestObject> binary = new BinaryOperator<TestObject>() {
@Override
public TestObject apply(TestObject t, TestObject u) {
return t;
}
};
正如您在匿名实现中看到的,它需要两个TestObject参数并返回一个TestObject对象。为了通过使用::操作符来满足这个条件,我们可以从一个静态方法开始:
public class TestObject {
public static final TestObject testStatic(TestObject t, TestObject t2) {
return t;
}
}
然后调用:
BinaryOperator<TestObject> binary = TestObject::testStatic;
它编译得很好。如果我们需要实例方法呢?让我们用一个实例方法更新TestObject:
public class TestObject {
public final TestObject testInstance(TestObject t, TestObject t2) {
return t;
}
public static final TestObject testStatic(TestObject t, TestObject t2) {
return t;
}
}
现在我们可以像下面这样访问实例:
TestObject testObject = new TestObject();
BinaryOperator<TestObject> binary = testObject::testInstance;
这段代码编译得很好,但下面的代码不行:
BinaryOperator<TestObject> binary = TestObject::testInstance;
我的Eclipse告诉我“不能从类型TestObject中对非静态方法testInstance(TestObject, TestObject)进行静态引用…”
很好。它是一个实例方法,但如果我们重载testInstance,如下所示:
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;
}
}
和电话:
BinaryOperator<TestObject> binary = TestObject::testInstance;
代码将正常编译。因为它将用一个参数而不是双参数调用testInstance。好的,那么两个参数发生了什么?让我们打印出来看看:
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;
}
}
它将输出:
1418481495
303563356
Test instance called. this.hashCode:1418481495
Given parameter hashCode:303563356
好,JVM足够聪明,可以调用param1.testInstance(param2)。我们可以从其他资源中使用testInstance而不是TestObject吗?例如:
public class TestUtil {
public final TestObject testInstance(TestObject t) {
return t;
}
}
和电话:
BinaryOperator<TestObject> binary = TestUtil::testInstance;
它只是不编译,编译器会说:“类型TestUtil没有定义testInstance(TestObject, TestObject)”。
因此,如果静态引用不是同一类型,编译器将查找它。那么多态性呢?如果我们删除最后的修饰符,并添加SubTestObject类:
public class SubTestObject extends TestObject {
public final TestObject testInstance(TestObject t) {
return t;
}
}
和电话:
BinaryOperator<TestObject> binary = SubTestObject::testInstance;
它也不会编译。编译器仍然会查找静态引用。但是下面的代码可以很好地编译,因为它通过了is-a测试:
public class TestObject {
public SubTestObject testInstance(Object t) {
return (SubTestObject) t;
}
}
BinaryOperator<TestObject> binary = TestObject::testInstance;