Java 8 prend-il en charge les fermetures?

Je suis confus. Je pensais que Java8 allait sortir de l’âge de pierre et commencer à soutenir les lambda / fermetures. Mais quand j’essaye:

public static void main(Ssortingng[] args) { int number = 5; ObjectCallback callback = () -> { return (number = number + 1); }; Object result = callback.Callback(); System.out.println(result); } 

il dit que ce number should be effectively final . C’est pas une fermeture je pense. Cela ressemble simplement à copier l’environnement par valeur plutôt que par référence.

Question bonus!

Est-ce que Android supportera les fonctionnalités de Java-8?

Pourquoi oh pourquoi Java? Pourquoi oh pourquoi.

Vous devrez tenir une longue discussion (privée) avec les membres de l’équipe Oracle Java concernés pour connaître la véritable réponse. (S’ils voulaient vous parler …)


Mais je soupçonne que c’est une combinaison de rétrocompatibilité et de contraintes de ressources de projet. Et le fait que l’approche actuelle soit «assez bonne» d’un sharepoint vue pragmatique.

La mise en œuvre de contextes de procédure en tant qu’objects de première classe (c’est-à-dire des fermetures) nécessite que la durée de vie de certaines variables locales dépasse le retour de l’appel à la méthode déclarante. Cela signifie que vous ne pouvez pas simplement les mettre sur la stack. Au lieu de cela, vous vous retrouvez avec une situation où certaines variables locales doivent être des champs d’un object tas. Cela signifie que vous avez besoin d’un nouveau type de classe cachée OU de modifications fondamentales de l’architecture JVM.

Bien qu’il soit techniquement possible d’implémenter ce genre de chose, le langage Java n’est pas un langage “de champ vert”. Un changement de nature nécessaire pour soutenir les “fermetures réelles” serait difficile:

  • Il faudrait énormément d’efforts de la part d’Oracle et des tiers pour mettre à jour toutes les chaînes d’outils. (Et nous ne parlons pas seulement des compilateurs. Il y a des débogueurs, des profileurs, des obfuscateurs, des frameworks d’ingénierie de bytecode, des frameworks de persistance …)

  • Ensuite, certains de ces changements pourraient avoir un impact sur la rétrocompatibilité des millions d’applications Java déployées existantes.

  • Il y a un impact potentiel sur d’ autres langages, etc. qui exploitent la JVM d’une manière ou d’une autre. Par exemple, Android dépend des fichiers d’architecture / bytecode JVM en tant que “langage de saisie” pour sa chaîne d’outils Davlik. Il existe des implémentations de langage pour Python, Ruby et divers langages fonctionnels que le code génère pour la plate-forme JVM.


En bref, les «vraies fermetures» à Java seraient une proposition effrayante pour toutes les personnes concernées. Le hack des “fermetures pour les finales” est un compromis pragmatique qui fonctionne, et qui est suffisamment bon dans la pratique.

Enfin, il est toujours possible que la ressortingction final soit supprimée dans une prochaine édition. (Je ne retiendrais pas mon souffle si…)


Est-ce que Android supportera les fonctionnalités de Java-8?

C’est impossible de répondre à moins que quelqu’un ait des connaissances crédibles. Et s’ils le faisaient, ils seraient fous de le révéler ici. Certes, Google n’a pas annoncé de support pour Java 8.

Mais la bonne nouvelle est que les extensions de syntaxe Java 7 sont désormais sockets en charge avec KitKat et les versions correspondantes d’Android Studio ou d’Eclipse ADT.

Vous devrez indiquer votre définition de «fermeture».

Pour moi, une “fermeture” est quelque chose (une fonction, un object ou une autre chose pouvant être exécutée comme une méthode) qui capture (“ferme”) une variable locale à partir de sa scope et qui peut utiliser cette variable dans son code, même lorsque la méthode de la fonction ou de l’object est exécutée ultérieurement, y compris lorsque la scope englobante n’existe plus. Dans différentes langues, il est possible de capturer une variable par valeur, par référence ou les deux.

Par cette définition, les classes anonymes Java (qui existent depuis Java 1.1) sont des fermetures, car elles peuvent faire référence à des variables locales à partir de sa scope.

Les Lambdas dans Java 8 sont essentiellement un cas particulier de classes anonymes (à savoir, une classe anonyme qui implémente une interface avec exactement une méthode (une “interface fonctionnelle”), et qui ne comporte aucune variable d’instance, et qui ne se réfère pas à elle-même (en utilisant this explicitement ou implicitement)). Tout lambda peut être ré-écrit dans une expression de classe anonyme équivalente. Donc, ce qui est dit ci-dessus s’applique également aux lambda.

C’est pas une fermeture je pense.

Eh bien, vous, monsieur, avez une définition désordonnée de «fermeture».

Je pense que la ressortingction final a des raisons techniques. Une expression lambda prend simplement la valeur du contexte de la méthode environnante car la référence vit sur la stack et ne survivra pas à la fin de la méthode.

Si vous mettez la valeur du contexte dans une référence, vous pouvez créer une “vraie” fermeture:

 import java.util.function.Supplier; public class CreatingAClosure { public static void main(Ssortingng[] args) { Supplier> mutterfunktion = () -> { int container[] = {0}; return () -> { container[0]++; return "Ich esse " + container[0] + " Kuchen."; }; }; Supplier essen = mutterfunktion.get(); System.out.println(essen.get()); System.out.println(essen.get()); System.out.println(essen.get()); } } 

Ausgabe:

 Ich esse 1 Kuchen. Ich esse 2 Kuchen. Ich esse 3 Kuchen. 

Au lieu d’un tableau, vous pouvez prendre n’importe quelle instance de n’importe quel object, car elle réside sur le tas et seule la référence à cette instance est conservée (finale) dans l’expression lambda.

Dans ce cas, la valeur de container est incluse dans mutterfunktion . Chaque appel à mutterfunktion crée une nouvelle instance référencée.

La valeur n’est pas accessible en dehors de la fonction (ce qui était très difficile à construire en Java 7 et avant). Étant donné que les expressions lambda sont implémentées en tant que références de méthode, il n’y a pas de classe interne impliquée dans cet exemple.

Vous pouvez également définir le container dans le contexte de la méthode et vous pourrez effectuer des modifications en dehors du lambda:

 public static void main(Ssortingng[] args) { int container[] = {0}; Supplier essen = () -> { container[0]++; return "Ich esse " + container[0] + " Kuchen."; }; System.out.println(essen.get()); System.out.println(essen.get()); container[0]++; System.out.println(essen.get()); } 

Ausgabe:

 Ich esse 1 Kuchen. Ich esse 2 Kuchen. Ich esse 4 Kuchen. 

La réponse à votre question sera donc “oui”.

Vous pouvez utiliser les références finales pour modifier l’état des variables déclarées dans une étendue externe, mais le résultat rest le même, l’état de la scope externe des fermetures n’est pas conservé et d’autres modifications apscopes à l’object référencé (par une référence finale) sont vu dans la fermeture.

 @Test public void clojureStateSnapshotTest() { Function wrapperFunc; wrapperFunc = (a) -> { // final reference final WrapLong outerScopeState = new WrapLong(); outerScopeState.aLong = System.currentTimeMillis(); System.out.println("outer scope state BEFORE: " + outerScopeState.aLong); Function closure = (b) -> { System.out.println("closure: " + outerScopeState.aLong); return b; }; outerScopeState.aLong = System.currentTimeMillis(); System.out.println("outer scope state AFTER: " + outerScopeState.aLong); // show correct snapshot state closure.apply(new Object()); return a; }; // init clojure wrapperFunc.apply(new Object()); } public class WrapLong { public long aLong = 0; } 

mais toujours amusant …