Oracle sort la Preview de Java SE 8 pour ARM, avec JavaFX
Les développeurs peuvent créer des applications pour Raspberry Pi

Les rubriques (actu, forums, tutos) de Développez
Tags
Réseaux sociaux


 Discussion forum

Retrouvez le dossier complet de la rédaction
Sur le même sujet
Le , par Hinault Romaric, Responsable Actualités
Mise à jour du 20/12/2012

Près de deux mois après la sortie de la préversion de Java SE 8, lors de la conférence JavaOne, Oracle publie une déclinaison de la plateforme de développement pour ARM.

La préversion de Java SE 8 pour ARM va permettre de développer des applications Java pour les systèmes embarqués et autres dispositifs reposant sur l’architecture ARM à l’instar du Raspberry Pi. Elle embarque une version de JavaFX (sur Linux), le framework de développement d’Applications Internet Riches.

Pour rappel, Java SE 8 introduira comme nouveautés : les expressions lambda, le moteur JavaScript Nashorn, les annotations, la nouvelle API « date and time » et bien plus.

La sortie de la version finale de la plateforme est prévue pour septembre 2013.




Télécharger Java SE 8 pour ARM

Source : Oracle

Et vous ?

Qu'en pensez-vous ?


Vous avez aimé cette actualité ? Alors partagez-la avec vos amis en cliquant sur les boutons ci-dessous :


 Poster une réponse

Avatar de Gugelhupf Gugelhupf
http://www.developpez.com
Membre émérite
le 20/12/2012 19:00
J'ai remarqué que les lambdas de Java 8 ressemblaient à celui de C++11.

Sympa d'inclure un Joda Time dans le standard.
Par contre j'aurai bien aimé qu'il y est les dates au format relatif.
Quelque chose du genre :
Code :
1
2
// Premier Mercredi du moins prochain 
DateTime d = new DateTime("first Wednesday of next month");
Pourrait-t-on avoir des informations plus précises sur les différentes nouveautés cités ?
Avatar de adiGuba adiGuba
http://www.developpez.com
Expert Confirmé Sénior
le 21/12/2012 10:00
Citation Envoyé par bouye  Voir le message
Ouaip c'est un peu pour ça que je trouve le truc moins efficace que les closures où il fallait juste transmettre le nom d'une fonction qui correspondait au type attendu. Ici, il faut utiliser une interface avec une seule méthode, c'est pas pratique pour tous les mouse et keyboard listener de AWT/Swing/JavaFX.

Le problème avec cette solution, c'est qu'elle ne s'applique pas bien à toutes les interfaces, et que cela pourrait engendrer des erreurs "bizarres".

En effet : que faire avec les méthodes non implémentées ?
leur attribuer un corps "vide" ?
remonter une exception (UnsupportedOperationException) ?

Si cela laisse le corps vide, ca marche bien avec les listeners, mais cela peut aboutir à des cas incohérent pour d'autre type d'interface. Image une méthode "add(E)" qui ne fasse rien. Tu penses stocker ton objet mais il n'en est rien !

Si cela remonte une exception, cela permet de détecter immédiatement le problème, mais cela empêche l'utilisation dans des listeners...

De même : que faire lorsqu'elles retournent une valeurs ?
retourner une valeur "nulle" (null, false, 0, 0.0 selon le cas)
retourner une exception ?

Bref dans tous ces cas là il est préférable d'adapter l'API avec une interface "mono-méthode"...

a++
Avatar de adiGuba adiGuba
http://www.developpez.com
Expert Confirmé Sénior
le 21/12/2012 11:35
Au passage je viens de découvrir une petit modif anodine mais bien sympathique dans l'API Logger.

En général on effectue un log de la manière suivante :
Code :
	LOG.log(Level.INFO, "Message " + args);
Toutefois ce code à un défaut, c'est que le message est généré AVANT l'appel de la méthode log(), et qu'il sera donc construit en mémoire même lorsque les logs de ce niveau sont désactivé.

Pour pallier à cela on utiliser généralement un if pour vérifier le niveau :
Code :
1
2
3
	if (LOG.isLoggable(Level.INFO)) { 
		LOG.log(Level.INFO, "Message " + args); 
	}


Dans la preview du JDK8 on peut désormais utiliser un Supplier<String> pour générer le message.
Supplier<T> est juste défini comme cela :
Code :
1
2
3
public interface Supplier<T> { 
    public T get(); 
}

L'intérêt c'est qu'on peut donc utiliser une expression lambda pour la génération du message :
Code :
	LOG.log(Level.INFO, () -> "Message " + args );
Du coup il est inutile de vérifier le niveau avant l'appel, puisque le message ne sera généré que lors de l'appel de la méthode get() du Supplier, et uniquement lorsque le niveau de log correspondant est activé...

a++
Avatar de bouye bouye
http://www.developpez.com
Modérateur
le 22/12/2012 14:42
Heu, j'ai toujours du mal avec leur notation à savoir à quel moment sont faits les allocations des closures/classes anonymes. Encore plus quand c'est sensé produire du bytecode compatible avec la version antérieure. Tu es bien sur de ce que tu avances ?
Avatar de adiGuba adiGuba
http://www.developpez.com
Expert Confirmé Sénior
le 22/12/2012 16:08
Citation Envoyé par bouye  Voir le message
Heu, j'ai toujours du mal avec leur notation à savoir à quel moment sont faits les allocations des closures/classes anonymes. Encore plus quand c'est sensé produire du bytecode compatible avec la version antérieure. Tu es bien sur de ce que tu avances ?

Oui j'ai testé cela dans l'early-access JDK8+lambda : http://jdk8.java.net/lambda/

Sinon cela ne produit pas du bytecode compatible dans le sens où il faudra à tout prix une JVM 1.8 pour faire tourner le code.

En fait le compilateur va générer une méthode statique qui contiendra le code de la lambda, et va inclure un appel dynamique vers cette méthode via l'invokedynamic et les MethodHandle.

Grosso modo le code suivant :
Code :
1
2
3
public void method(String args) { 
	LOG.log(Level.INFO, () -> "Message " + args ); 
}

Va générer quelque chose de la sorte :
Code :
1
2
3
4
5
6
7
public void method(String args) { 
	LOG.log(Level.INFO, invokedynamic bootstrap[lambda$1]); 
} 
 
static String lambda$1(String args) { 
	return "Message " + args; 
}

Bien sûr l'instruction "invokedynamic bootstrap[lambda$1]" n'est pas du Java, mais une représentation d'un appel dynamique permettant de créer le lambda.

Lors de cet appel dynamique, la JVM va retourner un type "lambda proxy" qui "simulera" l'interface fonctionnelle (Supplier<T> dans cet exemple).
Et apparemment la JVM pourra effectuer là dessus des optimisations très importante en allant carrément jusqu'à remplacer l'appel directement dans le code à l'intérieur de la méthode appelée.

J'avoue ne pas tout comprendre bien précisément, mais cela a vraiment l'air très prometteur...

Malheureusement, il y a un "MAIS" ! Apparemment ils sont à la bourre sur l'implémentation de ce proxy lors de l'appel dynamique. Ainsi dans un premier temps l'appel dynamique va se contenter de créer une classe anonyme...

La version optimisé avec le proxy viendra plus tard...

Et si j'ai bien compris tout ceci concerne avant tous la JVM, donc il n'y aura pas besoin de recompiler...

a++
Avatar de bouye bouye
http://www.developpez.com
Modérateur
le 24/12/2012 6:52
Merci de la précision
Avatar de adiGuba adiGuba
http://www.developpez.com
Expert Confirmé Sénior
le 27/12/2012 12:01
Juste pour info, l'early-access Lambda est assez complète : http://jdk8.java.net/lambda/

Il y a de quoi bien s'amuser même si l'inference des types n'est pas finalisé (du coup il faut parfois caster explicitement, ce qui alourdit la syntaxe).

Quelques exemples vites fait :

1 - On peut effectuer des itérations internes sur les éléments Iterable :
Code :
1
2
3
4
5
6
7
8
9
10
        Iterable<String> iterable = ... 
 
       // Ceci : 
       for (String s : iterable) { 
            System.out.println(s); 
        } 
       // Peut-être remplacé par cela : 
        iterable.forEach((String s) -> { 
            System.out.println(s); 
        });
Par defaut cela revient à la même chose (c'est à dire à utiliser un Iterator), mais chaque implémentation peut définir sa propre implémentation de forEach() plus optimisé (c'est le cas par exemple d'ArrayList qui effectuera directement le parcours sur son tableau interne).

Au passage ce code peut être encore plus compacte, de diverses manières :
Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
        // Expression Lambda complète, dont l'expression peut prendre plusieurs lignes : 
        iterable.forEach((String s) -> { 
            System.out.println(s); 
        }); 
        // Lorsque le code de l'expression se résume à une ligne, 
        // on peut se passer des crochet et du point-virgule final { ... ;} 
        iterable.forEach((String s) -> System.out.println(s)); 
        // Le type du (ou des) paramètre est déduit du contexte 
        // il est donc possible de s'en passer : 
        iterable.forEach((s) -> System.out.println(s)); 
        // Lorsqu'on n'a qu'un seul et unique paramètre, 
        // on peut se passer des parenthèses : 
        iterable.forEach(s -> System.out.println(s));
Mieux encore, lorsqu'on se contente d’appeler une méthode, et que sa signature correspond à l'interface fonctionnelle, on peut remplacer l'expression Lambda par un pointeur de méthode (notez bien les doubles deux-points :: )
Code :
        iterable.forEach(System.out::println);

Cette méthode forEach() est encore plus intéressante sur les Map :
Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
        Map<String, Object> map = ... 
 
        // Avec un Iterator on se ballade plein de chose inutile : 
        for (Map.Entry<String,Object> e : map.entrySet()) { 
            String k = e.getKey(); 
            Object v = e.getValue(); 
            System.out.println(k + " = " + v); 
        } 
 
        // Alors qu'on peut aller droit à l'essentiel avec les expr. lambda : 
        map.forEach((k,v) -> { 
            System.out.println(k + " = " + v); 
        });


L'API de Collections profite des "Default Method" pour se doter de quelques méthodes utiles, exemple :
Code :
1
2
3
4
5
6
7
8
9
10
        List<String> list = ... 
         
        // Passer tous les éléments de la liste en majuscule : 
        list.replaceAll(String::toUpperCase); 
        // Supprimer tous les éléments de moins de 4 caractères : 
        list.removeAll(s -> s.length() < 4); 
        // Trier les éléments selon l'ordre naturel (compareTo()) : 
        list.sort(Comparators.naturalOrder()); 
        // Trier les éléments selon leurs tailles : 
        list.sort(Comparators.comparing(String::length));
Note : cette dernière ligne ne fonctionne pas encore à cause de l'inférence des types qui n'est pas finalisé, et pour le moment il faut spécifier explicitement le paramétrage Generics, ce qui ne sera plus le cas dans la version finale :
Code :
list.sort(Comparators.<String,Integer>comparing(String::length));


Mais le plus gros ajout vient des streams : lorsqu'on utilise un stream, on peut filtrer ou modifier les valeurs de la source de données, sans modifier cette source.

Quelques exemples (loin d'être exhaustif) :
Code :
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
        Collection<String> coll = ... 
 
        // On prend tous les éléments de 6 caractères, 
        // en ignorant les doublons, 
        // que l'on va trier par taille, 
        // puis convertir en majuscule, 
        // pour finalement afficher les valeurs : 
        coll.stream() 
                .filter(s -> s.length() < 6) 
                .uniqueElements() 
                .sorted(Comparators.<String, Integer>comparing(String::length)) 
                .map(String::toUpperCase) 
                .forEach(System.out::println); 
 
        // Même chose que ci-dessus, mais en stockant le résultat dans une ArrayList : 
        List<String> result = coll.stream() 
                .filter(s -> s.length() < 6) 
                .uniqueElements() 
                .sorted(Comparators.<String, Integer>comparing(String::length)) 
                .map(String::toUpperCase) 
                .into(new ArrayList<String>()); 
 
        // Afficher seulement 5 éléments, après le 10ième 
        // (cela permet de mettre en place un système de pagination) 
        coll.stream() 
                .slice(10, 5) 
                .forEach(System.out::println); 
 
        // Recherche de la valeur minimum, selon l'ordre naturel : 
        String min = coll.stream() 
                .min(Comparators.naturalOrder()) 
                .get();

La grosse force des "stream" étant de pouvoir paralléliser les traitements.
En effet il suffit d'utiliser la méthode parallelStream() à la place de stream() pour que le traitement soit effectuer en parallèle (si cela est possible bien entendu).

a++
Avatar de la.lune la.lune
http://www.developpez.com
Membre Expert
le 28/12/2012 8:23
adiGuba
Avatar de adiGuba adiGuba
http://www.developpez.com
Expert Confirmé Sénior
le 30/12/2012 13:04
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 :
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 :
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 :
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++
Avatar de adiGuba adiGuba
http://www.developpez.com
Expert Confirmé Sénior
le 30/12/2012 19:04
Citation Envoyé par la.lune  Voir le message
Là on vient d'apprendre l'ajout du mot clé "default"

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 :


Citation Envoyé par la.lune  Voir le message
mais peut-on avoir une méthode par défaut et static aussi?

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++
Avatar de Gugelhupf Gugelhupf
http://www.developpez.com
Membre émérite
le 31/12/2012 14:37
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 ?
Avatar de kinaesthesia kinaesthesia
http://www.developpez.com
Membre confirmé
le 31/12/2012 14:51
Citation Envoyé par Gugelhupf  Voir le message
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 ?

Ca ne compilera pas tout simplement car le compilo te dira automatiquement que ta classe fille qui implémente les deux interfaces possèdes deux fois la même méthodes ;-)
Avatar de Gugelhupf Gugelhupf
http://www.developpez.com
Membre émérite
le 31/12/2012 15:37
Ç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.
Avatar de adiGuba adiGuba
http://www.developpez.com
Expert Confirmé Sénior
le 31/12/2012 16:08
Citation Envoyé par Gugelhupf  Voir le message
abiGuba, quels seront les différences entre ces interfaces et une classe abstraite ?

Les interfaces ne peuvent pas contenir d'état car elles ne peuvent pas contenir d'attribut d'instance.

Citation Envoyé par Gugelhupf  Voir le message
Est-ce que c'est semblable aux Traits et Mixins ?

Oui cela s'en approche beaucoup.

Citation Envoyé par Gugelhupf  Voir le message
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 ?

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 :
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...

Citation Envoyé par Gugelhupf  Voir le message
Ça parait logique de ce point de vue, mais alors pourquoi la compilation est autorisée en C++ ? ...

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++
Avatar de la.lune la.lune
http://www.developpez.com
Membre Expert
le 01/01/2013 20:02
Citation Envoyé par adiGuba  Voir le message
Mieux encore, lorsqu'on se contente d’appeler une méthode, et que sa signature correspond à l'interface fonctionnelle, on peut remplacer l'expression Lambda par un pointeur de méthode (notez bien les doubles deux-points :: )
Code :
        iterable.forEach(System.out::println);

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:
Code :
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)
Je pense que ma question est claire
Avatar de adiGuba adiGuba
http://www.developpez.com
Expert Confirmé Sénior
le 01/01/2013 21:51
@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 :
Code :
1
2
3
4
5
    public default void forEach(Block<? super T> block) { 
        for (T t : this) { 
            block.accept(t); 
        } 
    }
Et Block fait partie du package java.util.function, qui inclut un ensemble d'interface fonctionnelle basique...
Grosso-modo elle est définie comme ceci :
Code :
1
2
3
4
5
public interface Block<T> { 
 
    public void accept(T t);  
 
}
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).

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 :
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 :
1
2
3
public interface Exemple { 
    public String method(); 
}


Je ne sais pas si tout est bien clair...

a++
Avatar de la.lune la.lune
http://www.developpez.com
Membre Expert
le 01/01/2013 23:10
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?
Avatar de adiGuba adiGuba
http://www.developpez.com
Expert Confirmé Sénior
le 02/01/2013 9:59
Citation Envoyé par la.lune  Voir le message
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.

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 :
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 :
Code :
public void actionPerformed(ActionEvent e);
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 :
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 :
Code :
1
2
3
public static void onEvent(ActionEvent e) { 
    ... 
}
On peut l'associer à un ActionListener puisque la signature de la méthode concorde bien à la signature void(ActionEvent) :
Code :
ActionListener listener = MaClasse::onEvent;


S'il s'agit d'une méthode d'instance :
Code :
1
2
3
4
5
6
public class MaClasse { 
 
    public void doSomething(ActionEvent e) { 
         ... 
    } 
}
On ne peut pas l'associer comme cela puisque l'instance se rajoute à la signature, qui devient void(MaClasse,ActionEvent).
Code :
ActionListener listener = MaClasse::doSomething; // ERREUR
Pour que la signature corresponde, il faut lui associer une instance, par exemple :
Code :
1
2
MaClasse cls = new MaClasse(); 
ActionListener listener = cls::doSomething; // OK
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...

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 :
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 :
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++
Avatar de adiGuba adiGuba
http://www.developpez.com
Expert Confirmé Sénior
le 02/01/2013 12:10
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 :
Code :
1
2
3
    List<User> list = .... 
 
    list.sort( Comparators.comparing(User::getFirstName) );
Avec Comparators.comparing() qui nous retourne un Comparator<User> basé sur le getter 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 :
Code :
1
2
3
 
    list.sort( Comparators.comparing(User::getFirstName) 
            .reverseOrder() );
Ou alors on peut enchainer plusieurs comparaisons qui seront traiter dans l'ordre.
Par exemple pour tirer par nom, prénom puis date de naissance on pourra faire ceci :
Code :
1
2
3
    list.sort( Comparators.comparing(User::getFirstName) 
            .thenComparing(User::getLastName) 
            .thenComparing(User::getBirthday) );


a++
Avatar de la.lune la.lune
http://www.developpez.com
Membre Expert
le 02/01/2013 22:22
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.
Avatar de adiGuba adiGuba
http://www.developpez.com
Expert Confirmé Sénior
le 03/01/2013 10:39
Citation Envoyé par la.lune  Voir le message
De plus j'ai une autre question sur la parallélisation dont tu as mentionné sur les multiples instruction dont on applique à une collection,

Juste une remarque : les opérations s'appliquent sur le flux (stream), mais n'affecte pas la collection en elle-même

Citation Envoyé par la.lune  Voir le message
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.

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

Citation Envoyé par la.lune  Voir le message
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.

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) :
Code :
1
2
3
public interface Block<T> { 
	public void accept(T t); 
}
Function<T, R> qui prend un paramètre T et retourne une valeur R.
Code :
1
2
3
public interface Function<T, R> { 
	public R apply(T t); 
}
Predicate<T> qui permet de tester un élément.
Code :
1
2
3
public interface Predicate<T> { 
    public boolean test(T t); 
}
Supplier<T> qui permet de fournir une valeur.
Code :
1
2
3
public interface Supplier<T> { 
    public T get(); 
}
etc...

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++
Avatar de la.lune la.lune
http://www.developpez.com
Membre Expert
le 03/01/2013 15:33
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.
Offres d'emploi IT
Administrateur Bases de Données
Stage
Stage & Co - Ile de France - Paris (75000)
Parue le 07/07/2014
Ux/ui interactive designer
CDI
MOBISKILL - Ile de France - Paris (75000)
Parue le 22/07/2014
Strasbourg - Ingénieur Développement JAVA H/F
CDI
Atos Technology Services - Alsace - Strasbourg
Parue le 08/07/2014

Voir plus d'offres Voir la carte des offres IT
 
 
 
 
Partenaires

PlanetHoster
Ikoula