L’opérateur double colon en Java 8

L'opérateur double colon en Java 8

1. Vue d'ensemble

Dans cet article rapide, nous allons discuter desdouble colon operator (::) dans Java 8 et passer en revue les scénarios dans lesquels l'opérateur peut être utilisé.

Lectures complémentaires:

Questions d'entretien d'embauche Java 8 (+ réponses)

Un ensemble de questions d’entrevue populaires liées à Java8 et bien sûr de réponses.

Read more

Guide To Java 8 Facultatif

Guide rapide et pratique sur Optional in Java 8

Read more

Nouvelles fonctionnalités de Java 8

Une brève introduction aux nouvelles fonctionnalités de Java 8; l'accent est mis sur les méthodes d'interface statique et par défaut, les références de méthode statique et Facultatif.

Read more

2. De Lambdas à Double Colon Operator

Avec les expressions Lambdas, nous avons vu que le code peut devenir très concis.

Par exemple, pourcreate a comparator, la syntaxe suivante suffit:

Comparator c = (Computer c1, Computer c2) -> c1.getAge().compareTo(c2.getAge());

Ensuite, avec inférence de type:

Comparator c = (c1, c2) -> c1.getAge().compareTo(c2.getAge());

Mais pouvons-nous rendre le code ci-dessus encore plus expressif et lisible? Regardons:

Comparator c = Comparator.comparing(Computer::getAge);

Nous avons utilisé l'opérateur:: comme raccourci pour les lambdas appelant une méthode spécifique - par nom. Et au final, le résultat est bien sûr une syntaxe encore plus lisible.

3. Comment ça marche?

En termes très simples, lorsque nous utilisons une référence de méthode - la référence cible est placée avant le délimiteur:: et le nom de la méthode est fourni après.

Par exemple:

Computer::getAge;

Nous examinons une référence de méthode à la méthodegetAge définie dans la classeComputer.

Nous pouvons alors utiliser cette fonction:

Function getAge = Computer::getAge;
Integer computerAge = getAge.apply(c1);

Notez que nous référençons la fonction - puis l'appliquons au bon type d'argument.

4. Références de méthode

Nous pouvons faire bon usage de cet opérateur dans de nombreux scénarios.

4.1. Une méthode statique

Tout d'abord, nous allons utilisera static utility method:

List inventory = Arrays.asList(
  new Computer( 2015, "white", 35), new Computer(2009, "black", 65));
inventory.forEach(ComputerUtils::repair);

4.2. Une méthode d'instance d'un objet existant

Ensuite, regardons un scénario intéressant -referencing a method of an existing object instance.

Nous allons utiliser la variableSystem.out - un objet de typePrintStream qui prend en charge la méthodeprint:

Computer c1 = new Computer(2015, "white");
Computer c2 = new Computer(2009, "black");
Computer c3 = new Computer(2014, "black");
Arrays.asList(c1, c2, c3).forEach(System.out::print);

4.3. Une méthode d'instance d'un objet arbitraire d'un type particulier

Computer c1 = new Computer(2015, "white", 100);
Computer c2 = new MacbookPro(2009, "black", 100);
List inventory = Arrays.asList(c1, c2);
inventory.forEach(Computer::turnOnPc);

Comme vous pouvez le voir, nous référençons la méthodeturnOnPc non pas sur une instance spécifique, mais sur le type lui-même.

À la ligne 4, la méthode d'instanceturnOnPc sera appelée pour chaque objet deinventory.

Et cela signifie naturellement que - pourc1 la méthodeturnOnPc sera appelée sur l'instanceComputer et pourc2 sur l'instanceMacbookPro.

4.4. Une super méthode d'un objet particulier

Supposons que vous ayez la méthode suivante dans la superclasseComputer:

public Double calculateValue(Double initialValue) {
    return initialValue/1.50;
}

et celui-ci dans la sous-classeMacbookPro:

@Override
public Double calculateValue(Double initialValue){
    Function function = super::calculateValue;
    Double pcValue = function.apply(initialValue);
    return pcValue + (initialValue/10) ;
}

Un appel à la méthodecalculateValue sur une instanceMacbookPro:

macbookPro.calculateValue(999.99);

produira également un appel àcalculateValue sur la superclasseComputer.

5. Références constructeur

5.1. Créer une nouvelle instance

Référencer un constructeur pour instancier un objet peut être assez simple:

@FunctionalInterface
public interface InterfaceComputer {
    Computer create();
}

InterfaceComputer c = Computer::new;
Computer computer = c.create();

Et si vous avez deux paramètres dans un constructeur?

BiFunction c4Function = Computer::new;
Computer c4 = c4Function.apply(2013, "white");

Si les paramètres sont trois ou plus, vous devez définir une nouvelle interface fonctionnelle:

@FunctionalInterface
interface TriFunction {
    R apply(A a, B b, C c);
    default  TriFunction andThen( Function after) {
        Objects.requireNonNull(after);
        return (A a, B b, C c) -> after.apply(apply(a, b, c));
    }
}

Ensuite, initialisez votre objet:

TriFunction  c6Function = Computer::new;
Computer c3 = c6Function.apply(2008, "black", 90);

5.2. Créer un tableau

Enfin, voyons comment créer un tableau d'objetsComputer avec cinq éléments:

Function  computerCreator = Computer[]::new;
Computer[] computerArray = computerCreator.apply(5);

6. Conclusion

Comme nous commençons à le voir, l'opérateur double-virgule - introduit dans Java 8 - sera très utile dans certains scénarios, et en particulier en conjonction avec Streams.

Il est également très important de jeter un œil aux interfaces fonctionnelles pour mieux comprendre ce qui se passe dans les coulisses.

Lesource code complet pour l'exemple est disponible dansthis GitHub project - il s'agit d'un projet Maven et Eclipse afin qu'il puisse être importé et utilisé tel quel.