1135 Stimmen

:: (Doppelpunkt Doppelpunkt) Operator in Java 8

Ich habe den Java 8-Quellcode erkundet und diesen bestimmten Codeteil als sehr überraschend gefunden:

// In IntPipeline.java definiert
@Override
public final OptionalInt reduce(IntBinaryOperator op) {
    return evaluate(ReduceOps.makeInt(op));
}

@Override
public final OptionalInt max() {
    return reduce(Math::max); // Dies ist die überraschende Zeile
}

// In Math.java definiert
public static int max(int a, int b) {
    return (a >= b) ? a : b;
}

Ist Math::max ähnlich wie ein Methodenzeiger?
Wie wird eine normale static-Methode in einen IntBinaryOperator umgewandelt?

2voto

HRgiger Punkte 2680

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;

2voto

In älteren Java-Versionen können Sie anstelle von "::" oder Lambd stattdessen verwenden:

public interface Action {
    void execute();
}

public class ActionImpl implements Action {

    @Override
    public void execute() {
        System.out.println("Ausführung mit ActionImpl");
    }

}

public static void main(String[] args) {
    Action action = new Action() {
        @Override
        public void execute() {
            System.out.println("Ausführung mit anonymer Klasse");
        }
    };
    action.execute();

    //oder

    Action actionImpl = new ActionImpl();
    actionImpl.execute();
}

Oder indem Sie es an die Methode übergeben:

public static void doSomething(Action action) {
    action.execute();
}

2voto

Pramod Punkte 377

In Java 8 arbeitet der Streams Reducer als eine Funktion, die zwei Werte als Eingabe annimmt und das Ergebnis nach einiger Berechnung zurückgibt. Dieses Ergebnis wird in die nächste Iteration eingespeist.

Im Falle der Math:max Funktion gibt die Methode weiterhin das Maximum der beiden übergebenen Werte zurück und am Ende hast du die größte Zahl in der Hand.

2voto

Jude Niroshan Punkte 4061

return reduce(Math::max); ist nicht gleich zu return reduce(max());.

Aber es bedeutet so etwas wie das:

IntBinaryOperator myLambda = (a, b)->{(a >= b) ? a : b};//56 Tastenanschläge musste ich tippen -_-
return reduce(myLambda);

Du kannst einfach 47 Tastenanschläge sparen, wenn du es so schreibst:

return reduce(Math::max); // Nur 9 Tastenanschläge ^_^

1voto

Alfa khatoon Punkte 19

Zur Laufzeit verhalten sie sich genau gleich. Der Bytecode ist möglicherweise nicht der gleiche (im obigen Fall erzeugt er den gleichen Bytecode (kompilieren Sie oben und überprüfen Sie javaap -c;)).

Zur Laufzeit verhalten sie sich genau gleich. Die Methode (math::max) erzeugt die gleiche Mathematik (kompilieren Sie das obige und überprüfen Sie javap -c;)).

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X