Question :: (double colon) opérateur dans Java 8


J'explorais la source Java 8 et j'ai trouvé cette partie du code très surprenante:

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

Est Math::max quelque chose comme un pointeur de méthode? Comment est-ce qu'une normale static méthode se convertir en IntBinaryOperator?


747
2017-11-15 12:46


origine


Réponses:


Habituellement, on appelle le reduce méthode utilisant Math.max(int, int) comme suit:

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

Cela nécessite beaucoup de syntaxe pour appeler Math.max. C'est là que les expressions lambda entrent en jeu. Depuis Java 8, il est permis de faire la même chose de manière beaucoup plus courte:

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

Comment cela marche-t-il? Le compilateur java "détecte", que vous voulez implémenter une méthode qui accepte deux ints et renvoie un int. Ceci est équivalent aux paramètres formels de la seule et unique méthode d'interface IntBinaryOperator (le paramètre de la méthode reduce vous voulez appeler). Ainsi, le compilateur fait le reste pour vous - il suppose simplement que vous voulez mettre en œuvre IntBinaryOperator.

Mais comme Math.max(int, int) lui-même répond aux exigences formelles IntBinaryOperator, il peut être utilisé directement. Parce que Java 7 n'a pas de syntaxe qui permette de transmettre une méthode en tant qu'argument (vous ne pouvez transmettre que les résultats de la méthode, mais jamais les références de méthode), :: la syntaxe a été introduite dans Java 8 aux méthodes de référence:

reduce(Math::max);

Notez que ceci sera interprété par le compilateur, pas par la JVM au moment de l'exécution! Bien qu'il produise des bytecodes différents pour les trois extraits de code, ils sont sémantiquement égaux, de sorte que les deux derniers peuvent être considérés comme des versions courtes (et probablement plus efficaces) du IntBinaryOperator implémentation ci-dessus!

(Voir également Traduction des expressions Lambda)


826
2017-11-15 13:08



:: est appelé référence de méthode. C'est essentiellement une référence à une seule méthode. c'est-à-dire qu'il se réfère à une méthode existante par son nom.

Explication courte: Voici un exemple de référence à une méthode statique:

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 peut être transmis comme les références d'objets et se déclencher au besoin. En fait, il peut être parfaitement utilisé comme une référence à une méthode normale d'un objet et pas seulement static ceux

 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 ci-dessus est un interface fonctionnelle. Eh bien pour expliquer pleinement ::, il est important de comprendre l'interface fonctionnelle. Clairement, interface de fonction est une interface avec une seule méthode abstraite.

Par exemple: Runnable, Callable, ActionListener et donc.

Function ci-dessus est une interface fonctionnelle avec une seule méthode apply. Il prend un argument et produit un résultat.


La raison pour laquelle :: sont géniaux est car:

Les références de méthode sont des expressions qui ont le même traitement que les expressions lambda (...), mais au lieu de fournir un corps de méthode, elles renvoient une méthode existante par son nom.

C'est-à-dire comme écrire un corps lambda:

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

Vous pouvez simplement faire:

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

Au moment de l'exécution, ils se comportent exactement de la même manière. Le bytecode peut être / ne pas être le même (Pour le cas ci-dessus, il génère le même bytecode (compiler ci-dessus et vérifier javap -c))

Le seul critère majeur à satisfaire est: la méthode que vous fournissez doit avoir une signature similaire à la méthode de l'interface fonctionnelle que vous utilisez comme référence d'objet.

Ci-dessous est illégal:

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

square attend un argument et renvoie un double. get méthode dans Fournisseur attend un argument mais ne renvoie rien. Donc c'est une erreur.

La référence de méthode fait référence à une méthode de l'interface fonctionnelle (Comme mentionné, l'interface fonctionnelle ne peut avoir qu'une seule méthode).

Quelques autres exemples: accept méthode dans Consommateur prend une entrée mais ne retourne rien.

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

Au dessus getRandom ne prend aucun argument et renvoie un double. Donc, toute interface fonctionnelle qui satisfait aux critères de: ne prend aucun argument et retourne le double peut être utilisé.

Un autre exemple:

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

En cas de types paramétrés:

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;

Méthode Référence peut être obtenue dans différents styles, mais fondamentalement, ils ont tous la même signification et peuvent être simplement visualisés comme un lambda:

  1. Une méthode statique (ClassName::methName)
  2. Une méthode d'instance d'un objet particulier (instanceRef::methName)
  3. Une super méthode d'un objet particulier (super::methName)
  4. Une méthode d'instance d'un objet arbitraire d'un type particulier (ClassName::methName)
  5. Une référence de constructeur de classe (ClassName::new)
  6. Une référence de constructeur de tableau (TypeName[]::new)

Pour plus de référence: http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-final.html


382
2018-03-07 08:47



Oui c'est vrai. le :: L'opérateur est utilisé pour le référencement des méthodes. Donc, on peut extraire statique méthodes des classes en l'utilisant ou des méthodes à partir d'objets. Le même opérateur peut être utilisé même pour les constructeurs. Tous les cas mentionnés ici sont illustrés dans l'exemple de code ci-dessous.

La documentation officielle d'Oracle peut être trouvée ici.

Vous pouvez avoir une meilleure vue d'ensemble des changements du JDK 8 ce article. dans le Méthode / référencement de constructeur section un exemple de code est également fourni:

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



:: est un nouvel opérateur inclus dans Java 8 qui est utilisé pour référencer une méthode d'une classe existante. Vous pouvez référencer des méthodes statiques et des méthodes non statiques d'une classe.

Pour référencer des méthodes statiques, la syntaxe est:

ClassName :: methodName 

Pour référencer des méthodes non statiques, la syntaxe est

objRef :: methodName

Et

ClassName :: methodName

La seule condition pour référencer une méthode est que cette méthode existe dans une interface fonctionnelle, qui doit être compatible avec la référence de la méthode.

Les références de méthode, lorsqu'elles sont évaluées, créent une instance de l'interface fonctionnelle.

Trouvé sur: http://www.speakingcs.com/2014/08/method-references-in-java-8.html


22
2017-09-05 07:09



C'est une référence de méthode dans Java 8. La documentation d'Oracle est ici.

Comme indiqué dans la documentation ...

La référence de méthode Person :: compareByAge est une référence à un static   méthode.

Voici un exemple de référence à une méthode d'instance d'un   objet particulier:

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

La méthode référence myComparisonProvider :: compareByName appelle la méthode compareByName   cela fait partie de l'objet myComparisonProvider. Le JRE déduit le   arguments de type de méthode, qui dans ce cas sont (Personne, Personne).


18
2017-11-15 12:52



Il semble que c'est un peu tard, mais voici mes deux cents. UNE expression lambda est utilisé pour créer des méthodes anonymes. Il ne fait qu'appeler une méthode existante, mais il est plus clair de se référer directement à la méthode par son nom. Et référence de méthode nous permet de le faire en utilisant l'opérateur de référence de méthode :: .

Considérez la classe simple suivante où chaque employé a un nom et une note.

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

Supposons que nous ayons une liste d'employés retournés selon une méthode et que nous voulions trier les employés selon leur note. Nous savons que nous pouvons faire usage de classe anonyme comme:

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

où getDummyEmployee () est une méthode comme:

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

Maintenant nous savons que Comparateur est une interface fonctionnelle. UNE Interface fonctionnelle est celui avec exactement une méthode abstraite (bien qu'elle puisse contenir une ou plusieurs méthodes par défaut ou statiques). Nous pouvons donc utiliser l'expression lambda comme:

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

Il semble tout bon mais que faire si la classe Employee fournit également une méthode similaire:

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

Dans ce cas, l'utilisation du nom de la méthode sera plus claire. Par conséquent, nous pouvons directement faire référence à la méthode en utilisant la référence de la méthode comme:

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

Selon docs il existe quatre types de références de méthode:

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



:: Opérateur a été introduit dans java 8 pour les références de méthode. Une référence de méthode est la syntaxe abrégée d'une expression lambda qui n'exécute qu'une seule méthode. Voici la syntaxe générale d'une référence de méthode:

Object :: methodName

Nous savons que nous pouvons utiliser expressions lambda au lieu d'utiliser une classe anonyme. Mais parfois, l'expression lambda est vraiment juste un appel à une méthode, par exemple:

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

Pour rendre le code plus clair, vous pouvez transformer cette expression lambda en une référence de méthode:

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

4
2018-03-22 06:18



Le :: est connu comme références de méthode. Disons que nous voulons appeler une méthode calculatePrice de la classe Purchase. Ensuite, nous pouvons l'écrire comme:

Purchase::calculatePrice

Il peut également être vu comme une forme courte d'écriture de l'expression lambda car les références de méthode sont converties en expressions lambda.


3
2017-11-15 18:45



Au moment de l'exécution, ils se comportent exactement de la même manière. Le bytecode peut / ne doit pas être le même (pour Incase ci-dessus, il génère le même bytecode (complie ci-dessus et vérifie javaap -c;))

Au moment de l'exécution, ils se comportent exactement de la même façon. (Math :: max) ;, il génère le même math (complie ci-dessus et vérifie javap -c;))


2
2018-04-08 13:11



return reduce(Math::max); est INÉGAL à return reduce(max());

Mais ça veut dire quelque chose comme ça:

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

Tu peux juste enregistrer 47 frappes si vous écrivez comme ça

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

2
2017-07-23 05:57



Dans java-8 Streams Reducer dans les travaux simples est une fonction qui prend deux valeurs en entrée et renvoie le résultat après un certain calcul. ce résultat est alimenté à l'itération suivante.

dans le cas de la fonction Math: max, la méthode continue à renvoyer max de deux valeurs passées et à la fin vous avez le plus grand nombre en main.


2
2017-09-18 09:47