:: (Doppelpunkt) Operator in Java 8


956

Ich habe die Java 8- Quelle erkundet und fand diesen speziellen Teil des Codes sehr überraschend:

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

@Override
public final OptionalInt max() {
    return reduce(Math::max); //this is the gotcha line
}

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

Ist so Math::maxetwas wie ein Methodenzeiger? Wie wird eine normale staticMethode konvertiert IntBinaryOperator?


60
Es ist syntaktisch wichtig, dass der Compiler Schnittstellenimplementierungen basierend auf der von Ihnen bereitgestellten Funktion automatisch generiert (um die Verwendung des gesamten Lambda-Dings mit vorhandenen Codebasen zu vereinfachen).
Neet

4
java.dzone.com/articles/java-lambda-expressions-vs könnte helfen, hat nicht zu tief in das Thema geschaut
Pontus Backlund

8
@Neet ist es nicht gerade "syntaktischer Zucker", es sei denn, Sie können für was sagen. dh "x ist syntaktischer Zucker für y".
Ingo

6
@Ingo erstellt es jedes Mal ein neues Lambda-Objekt, wenn ich es benutze. TestingLambda$$Lambda$2/8460669und TestingLambda$$Lambda$3/11043253wurden auf zwei Anrufungen erstellt.
Narendra Pathai

13
Lambdas und Methodenreferenzen sind keine "einfachen alten anonymen inneren Klassen". Siehe programmers.stackexchange.com/a/181743/59134 . Ja, falls erforderlich, werden neue Klassen und Instanzen bei Bedarf im laufenden Betrieb erstellt, jedoch nur bei Bedarf.
Stuart Marks

Antworten:


1022

Normalerweise würde man die reduceMethode Math.max(int, int)wie folgt aufrufen :

reduce(new IntBinaryOperator() {
    int applyAsInt(int left, int right) {
        return Math.max(left, right);
    }
});

Das erfordert viel Syntax, um nur aufzurufen Math.max. Hier kommen Lambda-Ausdrücke ins Spiel. Seit Java 8 ist es erlaubt, dasselbe auf viel kürzere Weise zu tun:

reduce((int left, int right) -> Math.max(left, right));

Wie funktioniert das? Der Java-Compiler "erkennt", dass Sie eine Methode implementieren möchten, die zwei ints akzeptiert und eine zurückgibt int. Dies entspricht den formalen Parametern der einzigen Schnittstellenmethode IntBinaryOperator(dem Parameter der Methode, die reduceSie aufrufen möchten). Der Compiler erledigt also den Rest für Sie - er geht nur davon aus, dass Sie implementieren möchten IntBinaryOperator.

Da es jedoch Math.max(int, int)die formalen Anforderungen von erfüllt IntBinaryOperator, kann es direkt verwendet werden. Da Java 7 keine Syntax hat, mit der eine Methode selbst als Argument übergeben werden kann (Sie können nur Methodenergebnisse übergeben, aber niemals Methodenreferenzen), wurde die ::Syntax in Java 8 eingeführt, um Methoden zu referenzieren:

reduce(Math::max);

Beachten Sie, dass dies vom Compiler interpretiert wird, nicht von der JVM zur Laufzeit! Obwohl für alle drei Codefragmente unterschiedliche Bytecodes erzeugt werden, sind diese semantisch gleich, sodass die letzten beiden als kurze (und wahrscheinlich effizientere) Versionen der IntBinaryOperatorobigen Implementierung betrachtet werden können!

(Siehe auch Übersetzung von Lambda-Ausdrücken )


489

::heißt Methodenreferenz. Es ist im Grunde eine Referenz auf eine einzelne Methode. Das heißt, es bezieht sich auf eine vorhandene Methode mit Namen.

Kurze Erläuterung :
Nachfolgend finden Sie ein Beispiel für einen Verweis auf eine statische Methode:

class Hey {
     public static double square(double num){
        return Math.pow(num, 2);
    }
}

Function<Double, Double> square = Hey::square;
double ans = square.apply(23d);

squarekann wie Objektreferenzen weitergegeben und bei Bedarf ausgelöst werden. Tatsächlich kann es genauso einfach als Referenz auf "normale" Methoden von Objekten verwendet werden wie staticsolche. Zum Beispiel:

class Hey {
    public double square(double num) {
        return Math.pow(num, 2);
    }
}

Hey hey = new Hey();
Function<Double, Double> square = hey::square;
double ans = square.apply(23d);

Functionoben ist eine funktionale Schnittstelle . Um dies vollständig zu verstehen ::, ist es wichtig, auch funktionale Schnittstellen zu verstehen. Eine funktionale Schnittstelle ist eindeutig eine Schnittstelle mit nur einer abstrakten Methode.

Beispiele für funktionelle Schnittstellen gehören Runnable, Callableund ActionListener.

Functionoben ist eine funktionale Schnittstelle mit nur einer Methode : apply. Es nimmt ein Argument und erzeugt ein Ergebnis.


Der Grund, warum ::s großartig sind , ist folgender :

Methodenreferenzen sind Ausdrücke, die die gleiche Behandlung wie Lambda-Ausdrücke (...) haben, aber anstatt einen Methodenkörper bereitzustellen, verweisen sie auf eine vorhandene Methode mit Namen.

ZB anstatt den Lambda-Körper zu schreiben

Function<Double, Double> square = (Double x) -> x * x;

Sie können einfach tun

Function<Double, Double> square = Hey::square;

Zur Laufzeit squareverhalten sich diese beiden Methoden genau gleich. Der Bytecode kann derselbe sein oder auch nicht (obwohl für den obigen Fall der gleiche Bytecode generiert wird; kompilieren Sie den obigen und prüfen Sie mit javap -c).

Das einzige wichtige Kriterium, das erfüllt werden muss, ist: Die von Ihnen bereitgestellte Methode sollte eine ähnliche Signatur haben wie die Methode der Funktionsschnittstelle, die Sie als Objektreferenz verwenden .

Das Folgende ist illegal:

Supplier<Boolean> p = Hey::square; // illegal

squareerwartet ein Argument und gibt a zurück double. Die getMethode in Supplier gibt einen Wert zurück, akzeptiert jedoch kein Argument. Dies führt also zu einem Fehler.

Eine Methodenreferenz bezieht sich auf die Methode einer funktionalen Schnittstelle. (Wie bereits erwähnt, können funktionale Schnittstellen jeweils nur eine Methode haben.)

Einige weitere Beispiele: Die acceptMethode in Consumer nimmt eine Eingabe entgegen, gibt jedoch nichts zurück.

Consumer<Integer> b1 = System::exit;   // void exit(int status)
Consumer<String[]> b2 = Arrays::sort;  // void sort(Object[] a)
Consumer<String> b3 = MyProgram::main; // void main(String... args)

class Hey {
    public double getRandom() {
        return Math.random();
    }
}

Callable<Double> call = hey::getRandom;
Supplier<Double> call2 = hey::getRandom;
DoubleSupplier sup = hey::getRandom;
// Supplier is functional interface that takes no argument and gives a result

Oben getRandomnimmt kein Argument und gibt a zurück double. Daher kann jede funktionale Schnittstelle verwendet werden, die die Kriterien erfüllt: kein Argument nehmen und zurückgebendouble .

Ein anderes Beispiel:

Set<String> set = new HashSet<>();
set.addAll(Arrays.asList("leo","bale","hanks"));
Predicate<String> pred = set::contains;
boolean exists = pred.test("leo");

Bei parametrisierten Typen :

class Param<T> {
    T elem;
    public T get() {
        return elem;
    }

    public void set(T elem) {
        this.elem = elem;
    }

    public static <E> E returnSame(E elem) {
        return elem;
    }
}

Supplier<Param<Integer>> obj = Param<Integer>::new;
Param<Integer> param = obj.get();
Consumer<Integer> c = param::set;
Supplier<Integer> s = param::get;

Function<String, String> func = Param::<String>returnSame;

Methodenreferenzen können unterschiedliche Stile haben, aber im Grunde bedeuten sie alle dasselbe und können einfach als Lambdas visualisiert werden:

  1. Eine statische Methode ( ClassName::methName)
  2. Eine Instanzmethode eines bestimmten Objekts ( instanceRef::methName)
  3. Eine Supermethode eines bestimmten Objekts ( super::methName)
  4. Eine Instanzmethode eines beliebigen Objekts eines bestimmten Typs ( ClassName::methName)
  5. Eine Klassenkonstruktorreferenz ( ClassName::new)
  6. Eine Array-Konstruktorreferenz ( TypeName[]::new)

Weitere Informationen finden Sie unter http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-final.html .


6
Vielen Dank für die Erklärung. Zusammenfassend: '::' zum Extrahieren einer Methode, die eine FunctionalInterface (Lambda) erfüllt: ClassX :: staticMethodX oder instanceX :: instanceMethodX "
jessarah

55

Ja, das ist wahr. Der ::Operator wird für die Methodenreferenzierung verwendet. Man kann also statische Methoden aus Klassen extrahieren , indem man sie verwendet, oder Methoden aus Objekten. Der gleiche Operator kann auch für Konstruktoren verwendet werden. Alle hier genannten Fälle sind im folgenden Codebeispiel beispielhaft aufgeführt.

Die offizielle Dokumentation von Oracle finden Sie hier .

In diesem Artikel erhalten Sie einen besseren Überblick über die JDK 8-Änderungen . Im Abschnitt Referenzieren von Methoden / Konstruktoren wird auch ein Codebeispiel bereitgestellt:

interface ConstructorReference {
    T constructor();
}

interface  MethodReference {
   void anotherMethod(String input);
}

public class ConstructorClass {
    String value;

   public ConstructorClass() {
       value = "default";
   }

   public static void method(String input) {
      System.out.println(input);
   }

   public void nextMethod(String input) {
       // operations
   }

   public static void main(String... args) {
       // constructor reference
       ConstructorReference reference = ConstructorClass::new;
       ConstructorClass cc = reference.constructor();

       // static method reference
       MethodReference mr = cc::method;

       // object method reference
       MethodReference mr2 = cc::nextMethod;

       System.out.println(cc.value);
   }
}

Eine gute Erklärung ist die hier: doanduyhai.wordpress.com/2012/07/14/…
Olimpiu POP

1
@RichardTingle method(Math::max);ist Aufruf und Definition der Methode wäre wie public static void method(IntBinaryOperator op){System.out.println(op.applyAsInt(1, 2));}. So wird es benutzt.
Narendra Pathai

2
Für diejenigen, die mit C # vertraut sind, ist es ähnlich mit DelegateType d = new DelegateType (MethodName);
Adrian Zanescu

27

Es scheint etwas spät zu sein, aber hier sind meine zwei Cent. Ein Lambda-Ausdruck wird verwendet, um anonyme Methoden zu erstellen. Es wird lediglich eine vorhandene Methode aufgerufen, es ist jedoch klarer, die Methode direkt anhand ihres Namens zu referenzieren. Und die Methodenreferenz ermöglicht es uns, dies mit dem Methodenreferenzoperator zu tun ::.

Betrachten Sie die folgende einfache Klasse, in der jeder Mitarbeiter einen Namen und eine Besoldungsgruppe hat.

public class Employee {
    private String name;
    private String grade;

    public Employee(String name, String grade) {
        this.name = name;
        this.grade = grade;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getGrade() {
        return grade;
    }

    public void setGrade(String grade) {
        this.grade = grade;
    }
}

Angenommen, wir haben eine Liste der Mitarbeiter, die nach einer bestimmten Methode zurückgegeben wurden, und wir möchten die Mitarbeiter nach ihrer Besoldungsgruppe sortieren. Wir wissen, dass wir anonyme Klassen verwenden können als:

    List<Employee> employeeList = getDummyEmployees();

    // Using anonymous class
    employeeList.sort(new Comparator<Employee>() {
           @Override
           public int compare(Employee e1, Employee e2) {
               return e1.getGrade().compareTo(e2.getGrade());
           }
    });

Dabei ist getDummyEmployee () eine Methode wie:

private static List<Employee> getDummyEmployees() {
        return Arrays.asList(new Employee("Carrie", "C"),
                new Employee("Fanishwar", "F"),
                new Employee("Brian", "B"),
                new Employee("Donald", "D"),
                new Employee("Adam", "A"),
                new Employee("Evan", "E")
                );
    }

Jetzt wissen wir, dass der Komparator eine funktionale Schnittstelle ist. Eine funktionale Schnittstelle ist die mit genau einer abstrakten Methode (obwohl sie eine oder mehrere Standard- oder statische Methoden enthalten kann). Der Lambda-Ausdruck bietet eine Implementierung, @FunctionalInterfacesodass eine funktionale Schnittstelle nur eine abstrakte Methode haben kann. Wir können den Lambda-Ausdruck verwenden als:

employeeList.sort((e1,e2) -> e1.getGrade().compareTo(e2.getGrade())); // lambda exp

Es scheint alles gut zu sein, aber was ist, wenn die Klasse Employeeauch eine ähnliche Methode bereitstellt:

public class Employee {
    private String name;
    private String grade;
    // getter and setter
    public static int compareByGrade(Employee e1, Employee e2) {
        return e1.grade.compareTo(e2.grade);
    }
}

In diesem Fall ist die Verwendung des Methodennamens selbst klarer. Daher können wir uns direkt auf die Methode beziehen, indem wir die Methodenreferenz verwenden als:

employeeList.sort(Employee::compareByGrade); // method reference

Wie pro docs gibt vier Arten von Verfahren Referenzen sind:

+----+-------------------------------------------------------+--------------------------------------+
|    | Kind                                                  | Example                              |
+----+-------------------------------------------------------+--------------------------------------+
| 1  | Reference to a static method                          | ContainingClass::staticMethodName    |
+----+-------------------------------------------------------+--------------------------------------+
| 2  |Reference to an instance method of a particular object | containingObject::instanceMethodName | 
+----+-------------------------------------------------------+--------------------------------------+
| 3  | Reference to an instance method of an arbitrary object| ContainingType::methodName           |
|    | of a particular type                                  |                                      |  
+----+-------------------------------------------------------+--------------------------------------+
| 4  |Reference to a constructor                             | ClassName::new                       |
+------------------------------------------------------------+--------------------------------------+

25

::ist ein neuer in Java 8 enthaltener Operator, mit dem auf eine Methode einer vorhandenen Klasse verwiesen wird. Sie können statische und nicht statische Methoden einer Klasse referenzieren.

Für die Referenzierung statischer Methoden lautet die Syntax:

ClassName :: methodName 

Für das Verweisen auf nicht statische Methoden lautet die Syntax

objRef :: methodName

Und

ClassName :: methodName

Die einzige Voraussetzung für die Referenzierung einer Methode ist, dass die Methode in einer Funktionsschnittstelle vorhanden ist, die mit der Methodenreferenz kompatibel sein muss.

Wenn Methodenreferenzen ausgewertet werden, wird eine Instanz der Funktionsschnittstelle erstellt.

Gefunden unter: http://www.speakingcs.com/2014/08/method-references-in-java-8.html


22

Dies ist eine Methodenreferenz in Java 8. Die Oracle-Dokumentation finden Sie hier .

Wie in der Dokumentation angegeben ...

Die Methodenreferenz Person :: compareByAge ist eine Referenz auf eine statische Methode.

Das Folgende ist ein Beispiel für einen Verweis auf eine Instanzmethode eines bestimmten Objekts:

class ComparisonProvider {
    public int compareByName(Person a, Person b) {
        return a.getName().compareTo(b.getName());
    }

    public int compareByAge(Person a, Person b) {
        return a.getBirthday().compareTo(b.getBirthday());
    }
}

ComparisonProvider myComparisonProvider = new ComparisonProvider();
Arrays.sort(rosterAsArray, myComparisonProvider::compareByName); 

Die Methodenreferenz myComparisonProvider :: compareByName ruft die Methode compareByName auf, die Teil des Objekts myComparisonProvider ist. Die JRE leitet die Argumente des Methodentyps ab, in diesem Fall (Person, Person).


2
Die Methode 'compareByAge' ist jedoch nicht statisch.
Abbas

3
@abbas Noch ist compareByName. Daher greifen Sie über den Referenzoperator mit einem Objekt auf diese nicht statischen Methoden zu. Wenn sie statisch wären, könnten Sie den Klassennamen wie ComparisionProvider :: someStaticMethod
Seshadri R

6

:: Der Operator wurde in Java 8 für Methodenreferenzen eingeführt. Eine Methodenreferenz ist die Kurzsyntax für einen Lambda-Ausdruck, der nur EINE Methode ausführt. Hier ist die allgemeine Syntax einer Methodenreferenz:

Object :: methodName

Wir wissen, dass wir Lambda-Ausdrücke anstelle einer anonymen Klasse verwenden können. Aber manchmal ist der Lambda-Ausdruck wirklich nur ein Aufruf einer Methode, zum Beispiel:

Consumer<String> c = s -> System.out.println(s);

Um den Code klarer zu gestalten, können Sie diesen Lambda-Ausdruck in eine Methodenreferenz umwandeln:

Consumer<String> c = System.out::println;

3

Das :: wird als Methodenreferenz bezeichnet. Nehmen wir an, wir möchten eine berechnePreismethode der Klasse Purchase aufrufen. Dann können wir es schreiben als:

Purchase::calculatePrice

Es kann auch als Kurzform zum Schreiben des Lambda-Ausdrucks angesehen werden, da Methodenreferenzen in Lambda-Ausdrücke konvertiert werden.


Kann ich verschachtelte Methodenreferenzen erstellen? zB

Sie können auf diese Weise keine verschachtelte Methodenreferenz
Kirby

3

Ich fand diese Quelle sehr interessant.

Tatsächlich ist es das Lambda , das sich in einen Doppelpunkt verwandelt . Der Doppelpunkt ist besser lesbar. Wir folgen diesen Schritten:

SCHRITT 1:

// We create a comparator of two persons
Comparator c = (Person p1, Person p2) -> p1.getAge().compareTo(p2.getAge());

SCHRITT 2:

// We use the interference
Comparator c = (p1, p2) -> p1.getAge().compareTo(p2.getAge());

SCHRITT 3:

// The magic using method reference
Comparator c = Comparator.comparing(Person::getAge);

3
Scheint wie Person::getAge()sollte sein Person::getAge.
Qwertiy

2

return reduce(Math::max);ist NICHT GLEICH zureturn reduce(max());

Aber es bedeutet so etwas:

IntBinaryOperator myLambda = (a, b)->{(a >= b) ? a : b};//56 keystrokes I had to type -_-
return reduce(myLambda);

Sie können nur 47 Tastenanschläge speichern, wenn Sie so schreiben

return reduce(Math::max);//Only 9 keystrokes ^_^

2

Da hier viele Antworten das ::Verhalten gut erklärten , möchte ich zusätzlich klarstellen, dass der :: Operator nicht genau dieselbe Signatur wie die verweisende Funktionsschnittstelle haben muss, wenn er beispielsweise für Variablen verwendet wird . Nehmen wir an, wir brauchen einen BinaryOperator mit dem Typ TestObject . Auf traditionelle Weise wird es folgendermaßen implementiert:

BinaryOperator<TestObject> binary = new BinaryOperator<TestObject>() {

        @Override
        public TestObject apply(TestObject t, TestObject u) {

            return t;
        }
    };

Wie Sie in der anonymen Implementierung sehen, sind zwei TestObject-Argumente erforderlich, und es wird auch ein TestObject-Objekt zurückgegeben. 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 anrufen:

BinaryOperator<TestObject> binary = TestObject::testStatic;

Ok, es wurde gut kompiliert. Was ist, wenn wir eine Instanzmethode benötigen? Aktualisieren Sie TestObject mit der 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 wie folgt auf die Instanz zugreifen:

TestObject testObject = new TestObject();
BinaryOperator<TestObject> binary = testObject::testInstance;

Dieser Code wird gut kompiliert, aber unter einem nicht:

BinaryOperator<TestObject> binary = TestObject::testInstance;

Meine Sonnenfinsternis sagt mir: "Es kann kein statischer Verweis auf die nicht statische Methode testInstance (TestObject, TestObject) vom Typ TestObject ... erstellt werden."

Fair genug, es ist eine Instanzmethode, aber wenn wir testInstancewie 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 Ruf an:

BinaryOperator<TestObject> binary = TestObject::testInstance;

Der Code wird einfach gut kompiliert. Weil es testInstancemit einem einzelnen Parameter anstelle eines doppelten aufgerufen wird . Ok, was ist mit unseren beiden Parametern passiert? Lass uns ausdrucken und sehen:

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;
    }
}

Welches wird ausgegeben:

 1418481495  
 303563356  
 Test instance called. this.hashCode:1418481495
 Given parameter hashCode:303563356

Ok, JVM ist intelligent genug, um param1.testInstance (param2) aufzurufen. Können wir testInstancevon einer anderen Ressource verwenden, aber nicht von TestObject, dh:

public class TestUtil {

    public final TestObject testInstance(TestObject t){
        return t;
    }
}

Und Ruf an:

BinaryOperator<TestObject> binary = TestUtil::testInstance;

Es wird einfach nicht kompiliert und der Compiler sagt: "Der Typ TestUtil definiert nicht testInstance (TestObject, TestObject)" . Der Compiler sucht also nach einer statischen Referenz, wenn es sich nicht um denselben Typ handelt. Ok, was ist mit Polymorphismus? Wenn wir endgültige Modifikatoren entfernen und unsere SubTestObject- Klasse hinzufügen :

public class SubTestObject extends TestObject {

    public final TestObject testInstance(TestObject t){
        return t;
    }

}

Und Ruf an:

BinaryOperator<TestObject> binary = SubTestObject::testInstance;

Es wird auch nicht kompiliert, der Compiler sucht weiterhin nach statischen Referenzen. Aber der folgende Code wird gut kompiliert, da er einen Test besteht:

public class TestObject {

    public SubTestObject testInstance(Object t){
        return (SubTestObject) t;
    }

}

BinaryOperator<TestObject> binary = TestObject::testInstance;

* Ich lerne gerade, also habe ich herausgefunden, indem ich versucht habe zu sehen, zögern Sie nicht, mich zu korrigieren, wenn ich falsch liege


2

In Java-8 Streams Reducer ist in einfachen Arbeiten eine Funktion, die zwei Werte als Eingabe verwendet und nach einiger Berechnung das Ergebnis zurückgibt. Dieses Ergebnis wird in der nächsten Iteration eingespeist.

Im Fall der Funktion Math: max gibt die Methode immer wieder maximal zwei übergebene Werte zurück, und am Ende haben Sie die größte Anzahl zur Hand.


1

Zur Laufzeit verhalten sie sich genau gleich. Der Bytecode kann / kann nicht gleich sein.

Zur Laufzeit verhalten sie sich genau gleich. Method (math :: max); generiert die gleiche Mathematik (siehe oben und überprüfe javap -c;))


1

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

public interface Action {
    void execute();
}

public class ActionImpl implements Action {

    @Override
    public void execute() {
        System.out.println("execute with ActionImpl");
    }

}

public static void main(String[] args) {
    Action action = new Action() {
        @Override
        public void execute() {
            System.out.println("execute with anonymous class");
        }
    };
    action.execute();

    //or

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

Oder zur Methode übergehen:

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

1

So sehe ich hier jede Menge Antworten , die offen zu kompliziert sind, und das ist eine Untertreibung.

Die Antwort ist ziemlich einfach: :: Es wird als Methodenreferenz bezeichnet https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html

Damit ich nicht kopiere und einfüge, können Sie auf dem Link alle Informationen finden, wenn Sie zur Tabelle scrollen.


Lassen Sie uns nun einen kurzen Blick auf die Methodenreferenzen werfen:

A :: B ersetzt etwas den folgenden Inline-Lambda-Ausdruck : (params ...) -> AB (params ...)

Um dies mit Ihren Fragen zu korrelieren, ist es notwendig, einen Java-Lambda-Ausdruck zu verstehen. Welches ist nicht schwer.

Ein Inline-Lambda-Ausdruck ähnelt einer definierten funktionalen Schnittstelle (eine Schnittstelle mit nicht mehr und nicht weniger als einer Methode) . Lassen Sie uns einen kurzen Blick darauf werfen, was ich meine:

InterfaceX f = (x) -> x*x; 

InterfaceX muss eine funktionale Schnittstelle sein. Für jede funktionale Schnittstelle ist das einzige, was an InterfaceX für diesen Compiler wichtig ist, dass Sie das Format definieren:

InterfaceX kann eines davon sein:

interface InterfaceX
{
    public Integer callMe(Integer x);
}

oder dieses

interface InterfaceX
{
    public Double callMe(Integer x);
}

oder allgemeiner:

interface InterfaceX<T,U>
{
    public T callMe(U x);
}

Nehmen wir den ersten vorgestellten Fall und den zuvor definierten Inline-Lambda-Ausdruck.

Vor Java 8 hätten Sie es folgendermaßen definieren können:

 InterfaceX o = new InterfaceX(){
                     public int callMe (int x, int y) 
                       {
                        return x*x;
                       } };

Funktionell ist es das gleiche. Der Unterschied besteht eher darin, wie der Compiler dies wahrnimmt.

Nachdem wir uns den Inline-Lambda-Ausdruck angesehen haben, kehren wir zu den Methodenreferenzen (: :) zurück. Angenommen, Sie haben eine Klasse wie diese:

class Q {
        public static int anyFunction(int x)
             {
                 return x+5;
             } 
        }

Da die Methode anyFunctions dieselben Typen wie InterfaceX callMe hat , können wir diese beiden mit einer Methodenreferenz gleichsetzen.

Wir können es so schreiben:

InterfaceX o =  Q::anyFunction; 

und das ist gleichbedeutend damit:

InterfaceX o = (x) -> Q.anyFunction(x);

Eine coole Sache und ein Vorteil von Methodenreferenzen ist, dass sie zunächst typenlos sind, bis Sie sie Variablen zuweisen. Sie können sie also als Parameter an jede gleichwertig aussehende (mit denselben definierten Typen) Funktionsschnittstelle übergeben. Welches ist genau das, was in Ihrem Fall passiert


1

Die vorherigen Antworten sind ziemlich vollständig in Bezug auf was :: Methodenreferenz. Zusammenfassend bietet es eine Möglichkeit, auf eine Methode (oder einen Konstruktor) zu verweisen, ohne sie auszuführen, und erstellt bei der Auswertung eine Instanz der Funktionsschnittstelle, die den Zieltypkontext bereitstellt.

Im Folgenden finden Sie zwei Beispiele, um ein Objekt mit dem Maximalwert in einem ArrayListWITH und WITHOUT unter Verwendung der ::Methodenreferenz zu finden. Erklärungen finden Sie in den Kommentaren unten.


OHNE die Verwendung von ::

import java.util.*;

class MyClass {
    private int val;
    MyClass (int v) { val = v; }
    int getVal() { return val; }
}

class ByVal implements Comparator<MyClass> {
    // no need to create this class when using method reference
    public int compare(MyClass source, MyClass ref) {
        return source.getVal() - ref.getVal();
    }
}

public class FindMaxInCol {
    public static void main(String args[]) {
        ArrayList<MyClass> myClassList = new ArrayList<MyClass>();
        myClassList.add(new MyClass(1));
        myClassList.add(new MyClass(0));
        myClassList.add(new MyClass(3));
        myClassList.add(new MyClass(6));

        MyClass maxValObj = Collections.max(myClassList, new ByVal());
    }
}

Mit Hilfe von ::

import java.util.*;

class MyClass {
    private int val;
    MyClass (int v) { val = v; }
    int getVal() { return val; }
}

public class FindMaxInCol {
    static int compareMyClass(MyClass source, MyClass ref) {
        // This static method is compatible with the compare() method defined by Comparator. 
        // So there's no need to explicitly implement and create an instance of Comparator like the first example.
        return source.getVal() - ref.getVal();
    }

    public static void main(String args[]) {
        ArrayList<MyClass> myClassList = new ArrayList<MyClass>();
        myClassList.add(new MyClass(1));
        myClassList.add(new MyClass(0));
        myClassList.add(new MyClass(3));
        myClassList.add(new MyClass(6));

        MyClass maxValObj = Collections.max(myClassList, FindMaxInCol::compareMyClass);
    }
}

-1

Der Doppelpunkt, dh der ::Operator, wird in Java 8 als Methodenreferenz eingeführt . Die Methodenreferenz ist eine Form des Lambda-Ausdrucks dem die vorhandene Methode anhand ihres Namens .

Klassenname :: Methodenname

Ex:-

  • stream.forEach(element -> System.out.println(element))

Mit Double Colon ::

  • stream.forEach(System.out::println(element))
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.