adiGuba
Je viens de découvrir autre chose concernant les interfaces.
Pour rappel il y a les "default methods" qui permettront d'intégrer du code dans les interfaces :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7 public interface Demo { public void method1(); public default void method2() { System.out.println("method2"); } }Les classes qui implémentent cette interface pourront se contenter d'implémenter la méthode method1().
La méthode method2() est optionnelle et utilisera le code par défaut si elle n'est pas implémenté par la classe.
Mais ce n'est pas tout, on pourra utiliser des méthodes private, destiné à être utiliser dans les "default methods" afin de pouvoir mutualiser le code.
Par exemple :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 public interface Demo { public void method1(); public default void method2() { this.print("method2"); } public default void method3() { this.print("method3"); } private void print(String text) { System.out.println(text); } }
Enfin les interfaces pourront contenir des méthodes static :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 public interface Demo { public static void method() { /* ... */ } }
Attention toutefois car à l'heure actuelle ceci n'est implémenté que dans le compilateur. En clair cela va compiler normalement, mais cela risque de générer des erreurs à l'exécution...
Sources :
Enhancement: Add support for static interface methods
Enhancement: Add experimental support for private (instance) interface methods
a++
C'est le principe des "default's methods" qui permet de définir une implémentation par défaut dans une interface. Les classes qui l'implémente n'auront pas l'obligation d'implémenter la méthode.
Cette notion a eu plusieurs noms, et j'en ai pas mal parlé sur mon blog :
- Evolution des interfaces avec les « public defenders methods »
- « Extension Methods » de C# 3.0 VS « Defender Methods » de Java 7
Cela n'a pas de sens.
Les méthodes static d'une interface ne sont pas hérité ni rien. Elle doivent impérativement être appelé via la syntaxe NomDeLinterface.method().
a++
Bonjour,
abiGuba, quels seront les différences entre ces interfaces et une classe abstraite ?
Est-ce que c'est semblable aux Traits et Mixins ?
Comment résoudre le problème d'héritage multiple si une classe implémente 2 interfaces qui contiennent une méthode defaut avec le même nom mais un contenu différent ?
N'hésitez pas à consulter la FAQ Java, lire les cours et tutoriels Java, et à poser vos questions sur les forums d'entraide Java
Ma page Developpez | Mon profil Linkedin | Vous souhaitez me contacter ?Contacter Gokan EKINCI
Ça parait logique de ce point de vue, mais alors pourquoi la compilation est autorisée en C++ ? ...
Aussi quel différence y a-t-il maintenant entre une interface et une classe abstraite ?
Pour moi une interface était une classe 100% abstraite, je vais devoir changer ma définition de la chose.
N'hésitez pas à consulter la FAQ Java, lire les cours et tutoriels Java, et à poser vos questions sur les forums d'entraide Java
Ma page Developpez | Mon profil Linkedin | Vous souhaitez me contacter ?Contacter Gokan EKINCI
Les interfaces ne peuvent pas contenir d'état car elles ne peuvent pas contenir d'attribut d'instance.
Oui cela s'en approche beaucoup.
S'il n'y a aucun lien entre les deux interfaces, cela provoquera une erreur de compilation. Il faudra alors l'implémenter.
On peut bien sûr se contenter de réutiliser une implémentation d'une interface via une syntaxe spécifique, par exemple :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 interface A { public default void method() { System.out.println("A::method"); } } interface B { public default void method() { System.out.println("B::method"); } } class C implements A, B { public void method() { A.super.method(); } }
Par contre les règles sont plus souples, car si la méthode est implémenter dans une classe parente, cela prendra toujours la priorité sur les méthodes par défaut, qui comme leur nom l'indique, ne seront utilisé QUE dans le cas où la classe ne possèdent pas d'implémentation.
Là où cela est très intéressant, c'est que la résolution de la méthode est géré par la JVM au runtime. C'est à dire que cela permet d'ajouter une méthode à une interface sans avoir à recompiler toutes les classes qui l'implémentent...
Les règles d'héritage du C++ et de Java sont très différente.
En Java toutes les méthodes sont implicitement virtuelles et donc hérités par défaut, alors que c'est exactement l'inverse en C++.
Du coup les problèmes que l'on peut rencontrer via l'héritage multiple sont moins visible...
a++
J'ai une question ici, d'abord je comprend bien le comportement de cette expression, mais ma question c'est que veux-tu dire par 'interface fonctionnelle'? Tu peux détailler un peu pour qu'on comprenne le concept, je vais être un peut claire, si j'ai une interface que j'ai crée et une méthode static avec paramètre un objet, dont " iterable" itère sur une une collection de ces objets. alors peut-on faire cet astuce?
Je donne un code exemple:
Je pense que ma question est claire
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 public class A{ ..... } public interface I { public static void methode(A a){ ..... }}//interface I //Et puis dans un traitement peux-je faire: Iterable<A> iterable = ... iterable.forEach(I::methode)
@la.lune
Réponse courte : oui ton code fonctionne
Réponse longue :
Une interface fonctionnelle c'est juste une interface qui ne contient qu'une seule et unique méthode abstraite (sans compter les "default's methods" donc), comme par exemple Runnable.
Les lambdas et les références de méthode ne peuvent être convertis qu'en une interface fonctionnelle, à condition que leurs signatures correspondent.
Par exemple dans ce cas là la méthode Iterable::forEach() est définie comme ceci :
Et Block fait partie du package java.util.function, qui inclut un ensemble d'interface fonctionnelle basique...
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 public default void forEach(Block<? super T> block) { for (T t : this) { block.accept(t); } }
Grosso-modo elle est définie comme ceci :
Et c'est la signature de cette méthode qu'il faudra donc respecter avec la lambda (ou la référence de méthode).
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 public interface Block<T> { public void accept(T t); }
Si tu références une méthode static, alors la signature de cette méthode devra correspondre à la signature de l'interface. C'est le cas dans ton exemple donc cela marchera bien
Après tu as encore le cas particulier des méthodes d'instances, où tu as deux possibilités :
- Si tu références une méthode d'instance via son type, et la signature est alors modifié en rajoutant le type en tant que premier paramètre, ce qui permettra de passer l'instance lors de l'appel.
- Si tu la référence via une instance d'un objet, la signature n'est pas modifié et l'appel se fera sur l'instance indiqué.
Exemple Object::toString pourra être associer avec une interface fonctionnelle tel quel celle-ci :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3 public interface Exemple { public String method(Object o); }
A l'inverse pour une instance d'objet "o", o::toString sera associable avec une interface fonctionnelle comme ceci :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3 public interface Exemple { public String method(); }
Je ne sais pas si tout est bien clair...
a++
Oui j'ai compris en grog, même si j'avais fait juste fait une erreur en oubliant le mot "void" pour methode(..).
Mais là encore juste pour plus de précision, je me demande quelle est l'interface référencé ici quand tu dis "Si tu références une méthode static, alors la signature de cette méthode devra correspondre à la signature de l'interface", En quelle interface fonctionnelle la lamba sera converti?(l'interface dont sa signature correspond à celle de ma méthode static) Je n'ai pas bien saisi, où est la correspondance exactement?
Aussi, je suis je suis un peu perdu quand tu parles de correspondance entre signature de méthode et signature d'interface, ou bien tu veux parler de correspondance entre signature de méthode et signature de la méthode de l'interface fonctionnelle.
L'exemple que tu as donné c'est sur une interface fonctionnelle déjà existante, est ce le cas pour les autres? Ce que je n'arrive pas à saisir. Sur tes premières réponses tu parles de méthodes générés dynamiquement, donc si je ne me trompe pas, est-ce ma référence de méthode sera converti dynamiquement en interface fonctionnelle lors de la compilation?
Oui je voulais bien parler de la signature de la méthode de l'interface fonctionnelle.
Une lambda (ou une référence de méthode) ne peut être associé qu'à une interface fonctionnelle, et il faut que la signature correspondent à celle de la méthode :
Par exemple :
Code : Sélectionner tout - Visualiser dans une fenêtre à part ActionListener listener = (ActionEvent e) -> System.out.println("ActionEvent = " + e);L'expression lambda a la signature suivante : "void(ActionEvent)", ce qui correspond à la signature de la méthode de l'interface fonctionnelle ActionListener qui défini la méthode suivante :
Au passage le "Type Inférence" rentre en jeu, ce qui nous permet de ne pas spécifier le type du paramètre dans la lambda :
Code : Sélectionner tout - Visualiser dans une fenêtre à part public void actionPerformed(ActionEvent e);
Code : Sélectionner tout - Visualiser dans une fenêtre à part ActionListener listener = (e) -> System.out.println("ActionEvent = " + e);
Et c'est la même chose pour les références de méthode. Si on a une méthode définie comme ceci :
On peut l'associer à un ActionListener puisque la signature de la méthode concorde bien à la signature void(ActionEvent) :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3 public static void onEvent(ActionEvent e) { ... }
Code : Sélectionner tout - Visualiser dans une fenêtre à part ActionListener listener = MaClasse::onEvent;
S'il s'agit d'une méthode d'instance :
On ne peut pas l'associer comme cela puisque l'instance se rajoute à la signature, qui devient void(MaClasse,ActionEvent).
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6 public class MaClasse { public void doSomething(ActionEvent e) { ... } }
Pour que la signature corresponde, il faut lui associer une instance, par exemple :
Code : Sélectionner tout - Visualiser dans une fenêtre à part ActionListener listener = MaClasse::doSomething; // ERREUR
Dans ce cas là l'instance ("cls" dans l'exemple) sera utilisée pour l'appel de la méthode, qui conserve du coup sa signature originale...
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 MaClasse cls = new MaClasse(); ActionListener listener = cls::doSomething; // OK
Et pour les méthodes c'est la même chose. Les méthodes qui utiliseront les lambdas n'ont rien de spécial, si ce n'est qu'elles utilisent une interface fonctionnelle. Mais même si cette notion est "nouvelle", cela ne l'est pas vraiment car l'API standard est déjà pleine d'interface fonctionnelle...
Du coup on pourra utiliser les lambdas/références de méthode avec des APIs existante, du moment qu'elles utilisent une interface fonctionnelle :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3 MaClasse cls = new MaClasse(); JButton b = new JButton(); b.addActionListener(cls::doSomething);
C'est le "Type inference" qui se chargera de vérifier le tout, c'est à dire :
- Récupérer le type du paramètre de addActionListener() (qui est justement un ActionListener).
- Vérifier qu'il s'agit bien d'une interface fonctionnelle (ce qui est le cas puisqu'elle ne comporte qu'une seule méthode)
- Vérifier que la signature de la lambda/référence de méthode concorde bien avec la signature de la méthode de l'interface fonctionnelle.
Et en cas de surcharge, le compilo ira chercher la version de la méthode qui correspond.
A noter également l'existence de référence de constructeur (en utilisant ::new), par exemple :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13 // Création d'un ThreadFactory via une classe anonyme : Executors.newCachedThreadPool(new ThreadFactory() { @Override public Thread newThread(Runnable r) { return new Thread(r); } }); // Création d'un ThreadFactory via une référence de constructeur : Executors.newCachedThreadPool(Thread::new); // Ceci utilisera le constructeur Thread(Runnable), // qui correspond à la signature de la méthode de ThreadFactory
a++
Autre petite subtilité que j'ai découverte aujourd'hui : de nombreuses interfaces fonctionnelles (déjà existante ou non) sont enrichies de "default's method" permettant diverses choses.
Par exemple pour trier une liste d'User par nom, on pourra utiliser le code suivant :
Avec Comparators.comparing() qui nous retourne un Comparator<User> basé sur le getter getFirstName().
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3 List<User> list = .... list.sort( Comparators.comparing(User::getFirstName) );
Or l'interface Comparator est doté de nouvelle méthode permettant de créer des dérivés.
Par exemple on peut inverser l'ordre de tri très simplement :
Ou alors on peut enchainer plusieurs comparaisons qui seront traiter dans l'ordre.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3 list.sort( Comparators.comparing(User::getFirstName) .reverseOrder() );
Par exemple pour tirer par nom, prénom puis date de naissance on pourra faire ceci :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3 list.sort( Comparators.comparing(User::getFirstName) .thenComparing(User::getLastName) .thenComparing(User::getBirthday) );
a++
pour l'éclaircissement et merci encore de nous faire part de ces nouveautés
. Pour ma question là j'ai bien compris, surtout là je vois que si tu m'avait juste répondu en précisant que la signature de ma méthode satic void methode(A) correspond exactement à la signature de la méthode de l'interface fonctionnelle qu'attend un foreach qui est void(T) j'aurais tout compris et surpasser toutes les ambiguïtés.
De plus j'ai une autre question sur la parallélisation dont tu as mentionné sur les multiples instruction dont on applique à une collection, alors quand tu dis 'si cela est possible', je me pose la question possible dans le sens où certaines instructions dépendent des précédentes ou dans le sens de la capacité de l'ordinateur de paralléliser, et là aussi il y a deux option soit de la parallélisation juste en différents thread qui serait optimisée avec les architectures multi-core ou bien de la vrai parallélisation.
Ma dernière question c'est que tu parles d'interfaces de types qui ne sont pas encore finalisés dans une de tes réponses, alors peux-tu nous donner plus de détailles sur l'objet de ces interfaces et leur domaines d'applications.
Juste une remarque : les opérations s'appliquent sur le flux (stream), mais n'affecte pas la collection en elle-même
C'est juste que cela dépend des instructions que tu utilises, et de ce que tu veux faire.
Par exemple si tu veux stocker le résultat dans une collection, il faut que cette dernière soit thread-safe, car si ce n'est pas le cas cela engendrera des erreurs. Du coup même si tu choisis un stream parallel ce type d'opération sera en séquentiel par défaut (en fait c'est l'implémentation de la collection qui peut modifier cela).
En fait c'est juste un nouveau package java.util.function qui les interfaces fonctionnelles les plus courante.
Quelques exemples :
Block<T> qui permet d'exécuter un bout de code en prenant un objet en paramètre (c'est ce qui est utiliser par forEach() par exemple) :
Function<T, R> qui prend un paramètre T et retourne une valeur R.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3 public interface Block<T> { public void accept(T t); }
Predicate<T> qui permet de tester un élément.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3 public interface Function<T, R> { public R apply(T t); }
Supplier<T> qui permet de fournir une valeur.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3 public interface Predicate<T> { public boolean test(T t); }
etc...
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3 public interface Supplier<T> { public T get(); }
On y retrouve aussi des versions optimisés pour les types primitifs, ou des versions "Bi" prenant deux paramètres (et utilisé par les Map pour les clef/valeur).
Il n'y a rien d'exceptionnel là dedans, c'est juste pour fournir une base commune qui sera utilisable par tout le monde plutôt que de réécrire tout plein d'interface similaire dans chaque API
a++
pour ces réponses.
Là tu vient de m'éclairer sur ces fonctions. Merci aussi pour le lien, je vois bien les détailles sur ces fonctions. Vraiment j'ai hâte de voir la sortie de la JVM 1.8 pour commencer à coder nos programmes avec ces fonctionnalités très intéressantes, on aura à écrire peu de code, et surtout nous le fan de javafx on aura à tout simplifier lors de la sortie de Javafx 8 avec le JSE8 ( déjà la date est-elle officialisée pour quand?) . Là si j'ai bien compris à présent le boulot c'est de maîtriser aussi ces interfaces à utiliser pour écrire de bon expressions lambda(ou référence de méthode) et les autres interfaces.
On va bien voir la grande utilité dans des milliers de lignes de codes qui seront réduits disons jusqu’à 40% (possible). De plus ceci permettra aussi de bien structurer l'architecture du programme, on crée les interfaces( et leur implémentation si on) à part et fait les traitement à part en faisant référence aux méthodes. En tout cas c'est une évolution marquante du langage.
Il y a un nouveau build du JDK8/Lambda : http://jdk8.java.net/lambda/
Je n'ai pas vraiment eu le temps de le parcourir, mais l'interface Map s'est enrichie de plusieurs méthodes par défaut, en plus du forEach() dont j'avais déjà parlé !
Rien d'extraordinaire en soit, mais quand même des méthodes bien utile :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49 /* * Supprime un couple clef/valeur, * seulement si le couple existe. */ map.remove("KEY", "VALUE"); /* * Remplace la valeur associé à la clef, * seulement si cette clef existe déjà. */ map.replace("KEY", "NEWVALUE"); /* * Remplace la valeur d'un couple clef/valeur * seulement si le couple existe. */ map.replace("KEY", "VALUE", "NEWVALUE"); /* * Remplace toutes les valeurs de la Map, * par le résultat de l'expression. * Exemple : mettre toutes les valeurs en Majuscule : */ map.replaceAll((k,v) -> v.toUpperCase()); /* * Ajoute un couple clef/valeur, * seulement si la clef n'est pas déjà présente. */ map.putIfAbsent("KEY", "VALUE"); /* * Permet de récupérer ET de modifier * la valeur d'une clef dans la Map. */ String value = map.compute("KEY", (k,v) -> v.toUpperCase()); /* * Permet de modifier la valeur d'une clef, * seulement si le couple clef/valeur existe. */ String value = map.computeIfPresent("KEY", (k,v)->"new value"); /* * Permet de définir la valeur d'une clef, * lorsque le couple clef/valeur est absent. * Sinon on récupère la valeur existante. */ String value = map.computeIfAbsent("KEY", (k)->"empty");
A noter que ce "computeIfAbsent()" nous permettra d'implémenter une MultiMap en un rien de temps :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10 class MultiMap<K,V> extends HashMap<K,Collection<V>> { private Collection<V> creator(K key) { return new ArrayList<>(); } public void add(K key, V value) { this.computeIfAbsent(key, this::creator).add(value); } }L'appel add(K,V) permettra d'ajouter l'élément "value" dans la collection, en la créant si neccessaire.
a++
Bonjour,
Je lu l'article sur la syntaxe des lambdas (écrit par adiGuba).
1. J'aurais aimé savoir s'il y avait une différence entre le fait de préciser le type d'un argument ou non :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 // On ne précise pas le int (x) -> x + 1 // On précise le int (int x) -> x + 1
2. Aussi, si on utilise un lambda dans une classe, pourra-t-on automatiquement avoir accès au this dans le lambda (sans avoir à le faire passer par un paramètre) ?
En C++11 ou PHP5.3 il est possible de faire passer des variables externes au bloc lambda, on fait alors la distinction entre fonction anonyme et fermeture du lambda.
En C++11 par exemple :
En PHP5.3 par exemple :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10 // On retourne x + y [](int x, int y) -> int { return x + y; }; // On retourne x + y + z (z variable extérieur au bloc lambda) int z = 10; [z](int x, int y) -> int { return x + y + z; };
3. Qu'en est-t-il du Java pour les fermetures (utilisation de variable extérieur au bloc lambda) ?
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11 <?php // On retourne x + y function($x, $y){ return $x + $y; }; // On retourne x + y + z (z variable extérieur au bloc lambda) $z = 10; function($x, $y) use ($z){ return $x + $y + $z; };
Merci![]()
N'hésitez pas à consulter la FAQ Java, lire les cours et tutoriels Java, et à poser vos questions sur les forums d'entraide Java
Ma page Developpez | Mon profil Linkedin | Vous souhaitez me contacter ?Contacter Gokan EKINCI
Il n'y a aucune différence. S'il n'est pas précisé le type est déduite selon le contexte par le compilateur...
Oui, tout simplement en utilisant this :
Code : Sélectionner tout - Visualiser dans une fenêtre à part Runnable r = () -> this.method();
C'est tout à fait possible :
La seule restriction c'est que la variable doit être implicitement final.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6 // On retourne x + y IntBinatyOperator op1 = (x, y) -> x + y; // On retourne x + y + z (z variable extérieur au bloc lambda) int z = 10; IntBinatyOperator op1 = (x, y) -> x + y + z;
C'est à dire que la variable doit être définie et ne doit pas être modifié dans la lambda ou en dehors...
a++
Merci beaucoup adiGuba![]()
N'hésitez pas à consulter la FAQ Java, lire les cours et tutoriels Java, et à poser vos questions sur les forums d'entraide Java
Ma page Developpez | Mon profil Linkedin | Vous souhaitez me contacter ?Contacter Gokan EKINCI
Partager