Da viele Antworten hier das Verhalten von ::
gut erklärt haben, möchte ich zusätzlich klarstellen, dass der ::
Operator nicht genau dieselbe Signatur haben muss wie das referenzierte Funktionsinterface, wenn er für Instanzvariablen verwendet wird. Nehmen wir an, wir brauchen einen BinaryOperator, der den Typ TestObject hat. Auf herkömmliche Weise wird es wie folgt implementiert:
BinaryOperator binary = new BinaryOperator() {
@Override
public TestObject apply(TestObject t, TestObject u) {
return t;
}
};
Wie Sie sehen können, erfordert die anonyme Implementierung zwei TestObject-Argumente und gibt ebenfalls ein TestObject-Objekt zurück. Um diese Bedingung mit dem ::
Operator zu erfüllen, können wir mit einer statischen Methode beginnen:
public class TestObject {
public static final TestObject testStatic(TestObject t, TestObject t2) {
return t;
}
}
Und dann aufrufen:
BinaryOperator binary = TestObject::testStatic;
OK, es wurde gut kompiliert. Aber was ist, wenn wir eine Instanzmethode benötigen? Aktualisieren wir TestObject mit einer Instanzmethode:
public class TestObject {
public final TestObject testInstance(TestObject t, TestObject t2) {
return t;
}
public static final TestObject testStatic(TestObject t, TestObject t2) {
return t;
}
}
Jetzt können wir auf die Instanz wie folgt zugreifen:
TestObject testObject = new TestObject();
BinaryOperator binary = testObject::testInstance;
Dieser Code kompiliert gut, aber der folgende tut es nicht:
BinaryOperator binary = TestObject::testInstance;
Mein Eclipse sagt mir "Kann keine statische Referenz auf die nicht-statische Methode testInstance(TestObject, TestObject) von Typ TestObject machen..."
Gut. Es ist eine Instanzmethode, aber wenn wir testInstance
wie folgt überladen:
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;
}
}
Und aufrufen:
BinaryOperator binary = TestObject::testInstance;
Der Code wird einfach kompilieren. Denn es wird testInstance
mit einem einzelnen Parameter anstelle von zwei aufrufen. OK, was ist also mit unseren zwei Parametern passiert? Lassen Sie uns ausgeben und sehen:
public class TestObject {
public TestObject() {
System.out.println(this.hashCode());
}
public final TestObject testInstance(TestObject t) {
System.out.println("Test-Instanz aufgerufen. this.hashCode:" +
this.hashCode());
System.out.println("HashCode des übergebenen Parameters:" + 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;
}
}
Das wird folgendes ausgeben:
1418481495
303563356
Test-Instanz aufgerufen. this.hashCode:1418481495
HashCode des übergebenen Parameters:303563356
OK, also ist die JVM klug genug, um param1.testInstance(param2) aufzurufen. Können wir testInstance
von einer anderen Ressource, aber nicht von TestObject, verwenden? Z.B.:
public class TestUtil {
public final TestObject testInstance(TestObject t) {
return t;
}
}
Und rufen Sie auf:
BinaryOperator binary = TestUtil::testInstance;
Das wird einfach nicht kompilieren und der Compiler wird sagen: "Der Typ TestUtil definiert testInstance(TestObject, TestObject) nicht".
Der Compiler wird also nach einer statischen Referenz suchen, wenn es nicht derselbe Typ ist. OK, was ist mit Polymorphismus? Wenn wir die final Modifier entfernen und unsere SubTestObject Klasse hinzufügen:
public class SubTestObject extends TestObject {
public final TestObject testInstance(TestObject t) {
return t;
}
}
Und rufen Sie auf:
BinaryOperator binary = SubTestObject::testInstance;
Dies wird auch nicht kompilieren. Der Compiler wird immer noch nach einer statischen Referenz suchen. Aber der folgende Code wird gut kompilieren, da er den ist-ein Test besteht:
public class TestObject {
public SubTestObject testInstance(Object t) {
return (SubTestObject) t;
}
}
BinaryOperator binary = TestObject::testInstance;