Поскольку многие ответы здесь хорошо объясняли поведение ::
, я хотел бы уточнить, что оператор ::
не должен иметь точно такую же подпись, как ссылающийся функциональный интерфейс, если он используется для переменных экземпляра. Допустим, нам нужен BinaryOperator , который имеет тип TestObject. Традиционным способом это реализовано следующим образом:
BinaryOperator binary = new BinaryOperator() {
@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 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 binary = testObject::testInstance;
Этот код компилируется отлично, но ниже одного нет:
BinaryOperator binary = TestObject::testInstance;
Мое затмение говорит мне: «Невозможно сделать статическую ссылку на нестатический метод testInstance (TestObject, 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 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 binary = TestUtil::testInstance;
Он просто не компилируется, и компилятор скажет: « type TestUtil не определяет testInstance (TestObject, TestObject) ". Поэтому компилятор будет искать статическую ссылку, если это не тот же тип. Хорошо, что о полиморфизме? Если мы удалим финальные модификаторы и добавим наш класс SubTestObject:
public class SubTestObject extends TestObject {
public final TestObject testInstance(TestObject t){
return t;
}
}
И вызов:
BinaryOperator binary = SubTestObject::testInstance;
Он также не будет скомпилирован, компилятор по-прежнему будет искать статическую ссылку. Но ниже код будет компилироваться отлично, поскольку он проходит is-test:
public class TestObject {
public SubTestObject testInstance(Object t){
return (SubTestObject) t;
}
}
BinaryOperator binary = TestObject::testInstance;
* Я просто изучаю, поэтому я понял, что попробовал и посмотрю, не стесняйтесь исправлять меня, если я неправильно