35 votes

Quelle est la différence entre les classes anonymes en Java et les fermetures ?

Il semble que la classe anonyme fournisse la fonctionnalité de base de la fermeture, est-ce vrai ?

14voto

chubbsondubs Points 16075

Il n'y a pratiquement aucune différence. En fait, il existe un vieux dicton sur les fermetures et les objets. Les fermetures sont les objets du pauvre, et les objets sont les fermetures du pauvre. Les deux sont aussi puissants l'un que l'autre en termes de ce qu'ils peuvent faire. Nous ne nous disputons que sur l'expressivité.

En Java, nous modélisons les fermetures avec des objets anonymes. En fait, un peu d'histoire ici est qu'à l'origine Java avait la capacité de modifier la portée extérieure sans l'utilisation de final. Cela fonctionne bien pour les objets alloués dans la portée locale de la méthode, mais lorsqu'il s'agit de primitives, cela a suscité de nombreuses controverses. Les primitives sont allouées sur la pile et pour qu'elles puissent survivre à l'exécution de la méthode externe, Java devait allouer de la mémoire sur le tas et déplacer ces membres dans le tas. À l'époque, le ramassage des déchets était très récent et les gens n'avaient pas confiance en lui. La revendication était que Java ne devait pas allouer de mémoire sans instruction explicite du programmeur. Dans le but de trouver un compromis, Java a décidé d'utiliser le mot-clé final.

http://madbean.com/2003/mb2003-49/

Ce qui est intéressant, c'est que Java pourrait supprimer cette restriction et rendre l'utilisation du mot-clé final facultative maintenant que tout le monde est plus à l'aise avec le ramasseur de déchets et que cela pourrait être complètement compatible du point de vue du langage. Bien que la solution à ce problème soit simple, il suffit de définir des variables d'instance sur votre objet anonyme et vous pouvez les modifier autant que vous le souhaitez. En fait, cela pourrait être un moyen facile de mettre en œuvre des références de type fermeture à portée locale en ajoutant des variables d'instance publiques à la classe anonyme par le biais du compilateur, et en réécrivant le code source pour les utiliser à la place des variables de pile.

public Object someFunction() {
   int someValue = 0;

   SomeAnonymousClass implementation = new SomeAnonymousClass() {
       public boolean callback() {
           someValue++;
       }
   }
   implementation.callback();
   return someValue;
}

Serait réécrit en :

 public Object someFunction() {
   SomeAnonymousClass implementation = new SomeAnonymousClass() {
       public int someValue = 0;

       public boolean callback() {
           someValue++;
       }
   }
   implementation.callback();

   // all references to someValue could be rewritten to 
   // use this instance variable instead.
   return implementation.someValue;
}

Je pense que la raison pour laquelle les gens se plaignent des classes internes anonymes a plus à voir avec le typage statique que le typage dynamique. En Java, nous devons définir une interface commune à l'implémenteur de la classe anonyme et au code qui accepte la classe anonyme. Nous devons faire cela pour pouvoir vérifier le typage au moment de la compilation. Si nous avions des fonctions de première classe, Java devrait définir une syntaxe pour déclarer les paramètres et les types de retour d'une méthode comme un type de données afin de rester un langage à typage statique pour la sécurité des types. Cela serait presque aussi complexe que de définir une interface. (Une interface peut définir plusieurs méthodes, une syntaxe pour déclarer les méthodes de la 1ère classe ne serait que pour une seule méthode). On pourrait considérer cela comme une forme courte de syntaxe d'interface. Sous le capot, le compilateur pourrait traduire la notation abrégée en une interface au moment de la compilation.

Il y a beaucoup de choses qui pourraient être apportées à Java pour améliorer l'expérience de la classe anonyme sans pour autant abandonner le langage ou procéder à une chirurgie majeure.

9voto

Edwin Buck Points 33097

Dans la mesure où ils affectent tous deux le champ d'application autrement "privé", dans un sens très limité, oui. Cependant, il y a tellement de différences que la réponse pourrait tout aussi bien être non.

Étant donné que Java n'a pas la capacité de traiter les blocs de code comme de véritables valeurs R, les classes internes ne peuvent pas transmettre les blocs de code comme cela se fait généralement dans les continuations. Par conséquent, la technique de fermeture en tant que continuation est complètement absente.

Alors que la durée de vie d'une classe à collecter est prolongée par les personnes qui détiennent des classes internes (comme les fermetures qui maintiennent les variables en vie tout en étant liées à la fermeture), la capacité de Java à renommer par liaison est limitée pour se conformer à la syntaxe Java existante.

Et pour permettre aux threads de ne pas piétiner les données des autres en utilisant le modèle de contention des threads de Java, les classes internes sont encore plus limitées par l'accès aux données qui sont garanties de ne pas être bouleversées, c'est-à-dire les locales finales.

Ceci ignore complètement les autres classes internes (aka classes internes statiques) qui sont légèrement différentes dans le ressenti. En d'autres termes, elle aborde quelques éléments que les fermetures pourraient gérer, mais ne répond pas aux exigences minimales que la plupart des gens considèrent comme nécessaires pour être appelées une fermeture.

4voto

Peter Lawrey Points 229686

À mon avis, ils servent un objectif similaire, mais une fermeture est destinée à être plus concise et à fournir potentiellement plus de fonctionnalités.

Disons que vous voulez utiliser une variable locale en utilisant une classe anonyme.

final int[] i = { 0 };
final double[] d = { 0.0 };

Runnable run = new Runnable() { 
    public void run() {
        d[0] = i[0] * 1.5;
    }
};
executor.submit(run);

Les fermetures permettent d'éviter la plupart des codes passe-partout en vous permettant d'écrire uniquement ce qui est prévu.

int i = 0;
double d = 0.0;

Runnable run = { => d = i * 1.5; };
executor.submit(run);

ou même

executor.submit({ => d = i * 1.5; });

ou si les fermetures supportent les blocs de code.

executor.submit() {
    d = i * 1.5; 
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X