Frage :: (doppelter Doppelpunkt) Operator in Java 8


Ich habe die Java-8-Quelle erkundet und fand diesen besonderen 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 Math::max etwas wie ein Methodenzeiger? Wie geht es normal? static Methode wird in konvertiert IntBinaryOperator?


747
2017-11-15 12:46


Ursprung


Antworten:


Normalerweise würde man das anrufen reduce Methode mit Math.max(int, int) wie folgt:

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

Das erfordert eine Menge Syntax für das Anrufen Math.max. Hier kommen Lambda-Ausdrücke ins Spiel. Seit Java 8 ist es erlaubt, dasselbe viel kürzer zu machen:

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

Wie funktioniert das? Der Java-Compiler erkennt, dass Sie eine Methode implementieren wollen, die zwei akzeptiert ints und gibt eins zurück int. Dies entspricht den formalen Parametern der einzigen Interface-Methode IntBinaryOperator (der Parameter der Methode reduce Du möchtest anrufen). Der Compiler übernimmt also den Rest für Sie - es setzt lediglich voraus, dass Sie es implementieren möchten IntBinaryOperator.

Aber Math.max(int, int) selbst erfüllt die formalen Anforderungen von IntBinaryOperatorEs kann direkt verwendet werden. Da Java 7 über keine Syntax verfügt, die es ermöglicht, eine Methode selbst als Argument zu übergeben (Sie können nur Methodenergebnisse, aber keine Methodenreferenzen übergeben), lautet die :: Syntax wurde in Java 8 eingeführt, um Methoden zu referenzieren:

reduce(Math::max);

Beachten Sie, dass dies vom Compiler und nicht von der JVM zur Laufzeit interpretiert wird! Obwohl es für alle drei Codeschnipsel unterschiedliche Bytecodes erzeugt, sind sie semantisch gleich, so dass die letzten beiden als kurze (und wahrscheinlich effizientere) Versionen des IntBinaryOperator Implementierung oben!

(Siehe auch Übersetzung von Lambda Expressions)


826
2017-11-15 13:08



:: heißt Methodenreferenz. Es ist im Grunde ein Verweis auf eine einzige Methode. es bezieht sich auf eine existierende Methode nach Namen.

Kurze Erklärung: Im Folgenden 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);

square kann wie Objektreferenzen umgangen und bei Bedarf ausgelöst werden. In der Tat kann es perfekt als Referenz auf eine normale Methode eines Objekts und nicht nur verwendet werden static Einsen.

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

Function oben ist ein funktionale Schnittstelle. Gut zu erklären ::Es ist wichtig, die funktionale Schnittstelle zu verstehen. Deutlich, Funktionsschnittstelle ist eine Schnittstelle mit nur einer abstrakten Methode.

Beispielsweise: Runnable, Callable, ActionListener und so.

Function oben ist eine funktionale Schnittstelle mit nur einer Methode apply. Es braucht ein Argument und erzeugt ein Ergebnis.


Der Grund warum :: sind fantastisch ist weil:

Methodenreferenzen sind Ausdrücke, die die gleiche Behandlung wie Lambda-Ausdrücke (...) haben, aber anstatt einen Methodenkörper zu liefern, verweisen sie eine existierende Methode nach ihrem Namen.

Genau wie beim Schreiben eines Lambda-Körpers:

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

Sie können einfach tun:

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

Zur Laufzeit verhalten sie sich genau gleich. Der Bytecode kann / darf nicht derselbe sein (für den obigen Fall erzeugt er denselben Bytecode (kompiliere über und überprüfe) javap -c))

Die einzigen wichtigen Kriterien, die erfüllt werden müssen, sind: Die von Ihnen bereitgestellte Methode sollte eine ähnliche Signatur wie die Methode der funktionalen Schnittstelle haben, die Sie als Objektreferenz verwenden.

Unten ist illegal:

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

square erwartet ein Argument und gibt ein Double zurück. get Methode in Lieferant erwartet ein Argument, gibt aber nichts zurück. Es ist also ein Fehler.

Methodenreferenz bezieht sich auf eine Methode der funktionalen Schnittstelle (Wie erwähnt, kann die funktionale Schnittstelle nur eine Methode haben).

Einige weitere Beispiele: accept Methode in Verbraucher nimmt eine Eingabe, gibt aber 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

Über getRandom nimmt kein Argument und gibt ein Double zurück. Also jede funktionale Schnittstelle, die folgende Kriterien erfüllt: Nimm kein Argument und kehre doppelt zurück kann verwendet werden.

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

Methodenreferenz kann in verschiedenen Stilen erhalten werden, aber grundsätzlich bedeuten sie alle gleich und können einfach als Lambda 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)

Für weitere Referenz: http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-final.html


382
2018-03-07 08:47



Ja, das ist wahr. Das :: Der Operator wird für die Methodenreferenzierung verwendet. So kann man extrahieren statisch Methoden aus Klassen, indem Sie sie oder Methoden aus Objekten verwenden. Derselbe Operator kann auch für Konstrukteure verwendet werden. Alle hier genannten Fälle sind im folgenden Codebeispiel beispielhaft aufgeführt.

Die offizielle Dokumentation von Oracle kann gefunden werden Hier.

Sie können einen besseren Überblick über die JDK 8 Änderungen in erhalten Dies Artikel. In dem Referenzieren von Methoden / Konstruktoren Abschnitt ein Codebeispiel wird ebenfalls 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);
   }
}

49
2017-11-15 12:51



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

Für verweisende statische Methoden lautet die Syntax:

ClassName :: methodName 

Zum Verweisen auf nicht statische Methoden ist die Syntax

objRef :: methodName

Und

ClassName :: methodName

Die einzige Voraussetzung für den Verweis auf eine Methode ist, dass die Methode in einer funktionalen Schnittstelle existiert, die mit der Methodenreferenz kompatibel sein muss.

Methodenreferenzen erstellen beim Auswerten eine Instanz der funktionalen Schnittstelle.

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


22
2017-09-05 07:09



Dies ist eine Methodenreferenz in Java 8. Die Oracle-Dokumentation ist Hier.

Wie in der Dokumentation angegeben ...

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

Im Folgenden finden Sie ein Beispiel für einen Verweis auf eine Instanzmethode von a   bestimmtes Objekt:

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   das ist Teil des Objekts myComparisonProvider. Die JRE schließt die   Methodentypargumente, die in diesem Fall (Person, Person) sind.


18
2017-11-15 12:52



Es scheint ein wenig spät, aber hier sind meine zwei Cent. EIN Lambda-Ausdruck wird verwendet, um anonyme Methoden zu erstellen. Es ruft nur eine existierende Methode auf, aber es ist klarer, sich direkt auf die Methode zu beziehen. Und Methodenreferenz ermöglicht uns, dies mit dem Methodenreferenzoperator zu tun :: .

Betrachten Sie die folgende einfache Klasse, in der jeder Mitarbeiter einen Namen und eine Note 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 von Mitarbeitern, die nach einer Methode zurückgegeben werden, und wir möchten die Mitarbeiter nach ihrer Note sortieren. Wir wissen, dass wir davon Gebrauch machen können anonyme Klasse wie:

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

wo getDummyEmployee () eine Methode ist als:

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

Jetzt wissen wir das Vergleicher ist eine funktionale Schnittstelle. EIN Funktionelle Schnittstelle ist derjenige mit genau einer abstrakten Methode (obwohl er eine oder mehrere Standardmethoden oder statische Methoden enthalten kann). So können wir Lambda-Ausdruck als verwenden:

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

Es scheint alles gut, aber was ist, wenn die Klasse Employee bietet auch ähnliche Methode:

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 wird die Verwendung des Methodennamens selbst klarer. Daher können wir direkt auf die Methode verweisen, indem wir die Methodenreferenz verwenden als:

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

Nach Dokumente Es gibt vier Arten von Methodenreferenzen:

+----+-------------------------------------------------------+--------------------------------------+
|    | 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                       |
+------------------------------------------------------------+--------------------------------------+

9
2018-04-21 06:09



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

Object :: methodName

Wir wissen, dass wir es nutzen können Lambda-Ausdrücke anstatt eine anonyme Klasse zu verwenden. Aber manchmal ist der Lambda-Ausdruck wirklich nur ein Aufruf an irgendeine Methode, zum Beispiel:

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

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

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

4
2018-03-22 06:18