Délégués Java?

Le langage Java a-t-il des fonctionnalités de délégué, similaires à la manière dont C # prend en charge les delegates?

    Non, pas vraiment.

    Vous pourrez peut-être obtenir le même effet en utilisant la reflection pour obtenir les objects Méthode que vous pouvez appeler, et créer une interface avec une méthode unique «invoke» ou «execute», puis les instancier pour appeler la méthode. votre intéressé (c’est-à-dire en utilisant une classe interne anonyme).

    Vous pourriez également trouver cet article intéressant / utile: un programmeur Java regarde les delegates C #

    Selon précisément ce que vous voulez dire, vous pouvez obtenir un effet similaire (en faisant circuler une méthode) en utilisant le modèle de stratégie.

    Au lieu d’une ligne comme celle-ci déclarant une signature de méthode nommée:

    // C# public delegate void SomeFunction(); 

    déclarer une interface:

     // Java public interface ISomeBehaviour { void SomeFunction(); } 

    Pour les implémentations concrètes de la méthode, définissez une classe qui implémente le comportement:

     // Java public class TypeABehaviour implements ISomeBehaviour { public void SomeFunction() { // TypeA behaviour } } public class TypeBBehaviour implements ISomeBehaviour { public void SomeFunction() { // TypeB behaviour } } 

    Alors, où que vous ayez eu un délégué SomeFunction en C #, utilisez ISomeBehaviour une référence ISomeBehaviour :

     // C# SomeFunction doSomething = someMethod; doSomething(); doSomething = someOtherMethod; doSomething(); // Java ISomeBehaviour someBehaviour = new TypeABehaviour(); someBehaviour.SomeFunction(); someBehaviour = new TypeBBehaviour(); someBehaviour.SomeFunction(); 

    Avec les classes internes anonymes, vous pouvez même éviter de déclarer des classes nommées distinctes et les traiter presque comme de véritables fonctions de délégué.

     // Java public void SomeMethod(ISomeBehaviour pSomeBehaviour) { ... } ... SomeMethod(new ISomeBehaviour() { @Override public void SomeFunction() { // your implementation } }); 

    Cela ne devrait probablement être utilisé que lorsque l’implémentation est très spécifique au contexte actuel et ne bénéficierait pas de la réutilisation.

    Et puis, bien sûr, en Java 8, ces expressions deviennent essentiellement des expressions lambda:

     // Java 8 SomeMethod(() -> { /* your implementation */ }); 

    Petite histoire: non .

    introduction

    La dernière version de l’environnement de développement Microsoft Visual J ++ prend en charge une construction de langage appelée delegates ou références de méthodes liées . Cette construction, ainsi que les nouveaux mots-clés delegate et multicast introduits pour la prendre en charge, ne font pas partie du langage de programmation Java TM , spécifié par la spécification de langage Java et modifié par la spécification Inner Classes incluse dans la documentation du logiciel JDKTM 1.1. .

    Il est peu probable que le langage de programmation Java inclue jamais cette construction. Sun a déjà sérieusement envisagé de l’adopter en 1996, au sharepoint construire et de supprimer des prototypes fonctionnels. Notre conclusion était que les références de méthode liées sont inutiles et préjudiciables à la langue. Cette décision a été prise en consultation avec Borland International, qui avait déjà travaillé avec des références de méthodes liées dans Pascal Object Pascal.

    Nous pensons que les références de méthodes liées sont inutiles car une autre alternative de conception, les classes internes , fournit des fonctionnalités égales ou supérieures. En particulier, les classes internes prennent entièrement en charge les exigences de la gestion des événements de l’interface utilisateur et ont été utilisées pour implémenter une API d’interface utilisateur au moins aussi complète que les classes Windows Foundation.

    Nous pensons que les références de méthodes liées sont dangereuses car elles nuisent à la simplicité du langage de programmation Java et au caractère orienté object des API. Les références de méthode liées introduisent également des irrégularités dans la syntaxe du langage et les règles de scope. Enfin, ils diluent l’investissement dans les technologies VM, car les machines virtuelles doivent gérer des types de références et des méthodes d’association supplémentaires et disparates.

    Avez-vous lu ceci :

    Les delegates sont une construction utile dans les systèmes basés sur des événements. Essentiellement, les delegates sont des objects qui codent une dissortingbution de méthode sur un object spécifié. Ce document montre comment les classes internes Java fournissent une solution plus générique à ces problèmes.

    Qu’est-ce qu’un délégué? En réalité, il est très similaire à un pointeur vers la fonction membre, tel qu’utilisé en C ++. Mais un délégué contient l’object cible avec la méthode à appeler. Idéalement, ce serait bien de pouvoir dire:

    obj.registerHandler (ano.methodOne);

    ..et que la méthode methodOne serait appelée sur ano lorsqu’un événement spécifique était reçu.

    C’est ce que réalise la structure des delegates.

    Classes internes Java

    Il a été avancé que Java fournit cette fonctionnalité via des classes internes anonymes et n’a donc pas besoin de la construction Delegate supplémentaire.

     obj.registerHandler(new Handler() { public void handleIt(Event ev) { methodOne(ev); } } ); 

    À première vue, cela semble correct mais en même temps une nuisance. Parce que pour de nombreux exemples de traitement d’événements, la simplicité de la syntaxe de Delegates est très intéressante.

    Gestionnaire général

    Toutefois, si la programmation basée sur les événements est utilisée de manière plus généralisée, par exemple dans le cadre d’un environnement de programmation asynchrone général, l’enjeu est plus important.

    Dans une telle situation générale, il ne suffit pas d’inclure uniquement la méthode cible et l’instance d’object cible. En général, d’autres parameters peuvent être requirejs, déterminés dans le contexte lors de l’enregistrement du gestionnaire d’événements.

    Dans cette situation plus générale, l’approche java peut fournir une solution très élégante, en particulier lorsqu’elle est associée à l’utilisation de variables finales:

     void processState(final T1 p1, final T2 dispatch) { final int a1 = someCalculation(); m_obj.registerHandler(new Handler() { public void handleIt(Event ev) { dispatch.methodOne(a1, ev, p1); } } ); } 

    finale * finale * finale

    Vous avez votre attention?

    Notez que les variables finales sont accessibles depuis les définitions de méthodes de classe anonymes. Assurez-vous d’étudier attentivement ce code pour comprendre les ramifications. C’est potentiellement une technique très puissante. Par exemple, il peut être utilisé avec succès lors de l’enregistrement de gestionnaires dans MiniDOM et dans des situations plus générales.

    En revanche, la construction de Delegate ne fournit pas de solution à cette exigence plus générale et, en tant que telle, elle devrait être rejetée en tant qu’exemple sur lequel les conceptions peuvent être basées.

    Je sais que cet article est ancien, mais Java 8 a ajouté lambdas, et le concept d’une interface fonctionnelle, qui est une interface avec une seule méthode. Ensemble, ces fonctionnalités offrent des fonctionnalités similaires aux delegates C #. Voir ici pour plus d’informations, ou simplement google Java Lambdas. http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-final.html

    Non, mais ils sont facturables à l’aide de procurations et de reflection:

      public static class TestClass { public Ssortingng knockKnock() { return "who's there?"; } } private final TestClass testInstance = new TestClass(); @Test public void can_delegate_a_single_method_interface_to_an_instance() throws Exception { Delegator> knockKnockDelegator = Delegator.ofMethod("knockKnock") .of(TestClass.class) .to(Callable.class); Callable callable = knockKnockDelegator.delegateTo(testInstance); assertThat(callable.call(), is("who's there?")); } 

    La bonne chose à propos de cet idiome est que vous pouvez vérifier que la méthode déléguée existe et qu’elle a la signature requirejse, au moment où vous créez le délégateur (bien que pas à la compilation, malheureusement, bien qu’un plug-in FindBugs puisse help here), puis utilisez-le en toute sécurité pour déléguer à différentes instances.

    Voir le code karg sur github pour plus de tests et d’ implémentation .

    J’ai implémenté le support callback / delegate en Java en utilisant la reflection. Les détails et la source de travail sont disponibles sur mon site .

    Comment ça marche

    Il existe une classe de principe nommée Callback avec une classe nestede nommée WithParms. L’API qui a besoin du rappel prendra un object Callback comme paramètre et, si nécessaire, crée un Callback.WithParms en tant que variable de méthode. Comme un grand nombre d’applications de cet object seront récursives, cela fonctionne très proprement.

    Les performances étant toujours une priorité pour moi, je ne voulais pas être obligé de créer un tableau d’objects jetables pour contenir les parameters pour chaque appel – après tout, dans une grande structure de données, il pourrait y avoir des milliers d’éléments et dans un traitement de message scénario nous pourrions finir par traiter des milliers de structures de données par seconde.

    Pour être threadsafe, le tableau de parameters doit exister de manière unique pour chaque appel de la méthode API, et pour des raisons d’efficacité, le même doit être utilisé pour chaque appel du rappel; J’avais besoin d’un deuxième object qui serait bon marché à créer pour lier le rappel avec un tableau de parameters à invoquer. Mais, dans certains scénarios, l’invocateur aurait déjà un tableau de parameters pour d’autres raisons. Pour ces deux raisons, le tableau de parameters n’appartient pas à l’object Callback. Le choix de l’invocation (transmission des parameters sous forme de tableau ou d’objects individuels) appartient également à l’API en utilisant le rappel qui lui permet d’utiliser l’invocation la mieux adaptée à son fonctionnement interne.

    La classe nestede WithParms est donc facultative et remplit deux fonctions: elle contient le tableau d’object paramètre nécessaire pour les invocations de rappel et fournit 10 méthodes invoke () surchargées (de 1 à 10 parameters) qui chargent le tableau de parameters, puis invoquer la cible de rappel.

    Ce qui suit est un exemple utilisant un rappel pour traiter les fichiers dans une arborescence de répertoires. Il s’agit d’un passage de validation initial qui compte uniquement les fichiers à traiter et s’assure qu’aucune ne dépasse une taille maximale prédéterminée. Dans ce cas, nous créons simplement le rappel en ligne avec l’appel de l’API. Cependant, nous reflétons la méthode cible comme une valeur statique afin que la reflection ne soit pas effectuée à chaque fois.

     static private final Method COUNT =Callback.getMethod(Xxx.class,"callback_count",true,File.class,File.class); ... IoUtil.processDirectory(root,new Callback(this,COUNT),selector); ... private void callback_count(File dir, File fil) { if(fil!=null) { // file is null for processing a directory fileTotal++; if(fil.length()>fileSizeLimit) { throw new Abort("Failed","File size exceeds maximum of "+TextUtil.formatNumber(fileSizeLimit)+" bytes: "+fil); } } progress("Counting",dir,fileTotal); } 

    IoUtil.processDirectory ():

     /** * Process a directory using callbacks. To interrupt, the callback must throw an (unchecked) exception. * Subdirectories are processed only if the selector is null or selects the directories, and are done * after the files in any given directory. When the callback is invoked for a directory, the file * argument is null; * 

    * The callback signature is: *

     void callback(File dir, File ent);

    *

    * @return The number of files processed. */ static public int processDirectory(File dir, Callback cbk, FileSelector sel) { return _processDirectory(dir,new Callback.WithParms(cbk,2),sel); } static private int _processDirectory(File dir, Callback.WithParms cbk, FileSelector sel) { int cnt=0; if(!dir.isDirectory()) { if(sel==null || sel.accept(dir)) { cbk.invoke(dir.getParent(),dir); cnt++; } } else { cbk.invoke(dir,(Object[])null); File[] lst=(sel==null ? dir.listFiles() : dir.listFiles(sel)); if(lst!=null) { for(int xa=0; xa

    Cet exemple illustre la beauté de cette approche - la logique spécifique à l'application est extraite dans le rappel, et la corvée de la marche récursive dans une arborescence de répertoires est bien rangée dans une méthode d'utilitaire statique complètement réutilisable. Et nous n'avons pas à payer le prix de la définition et de l'implémentation d'une interface pour chaque nouvelle utilisation. Bien sûr, l'argument en faveur d' une interface est qu'il est beaucoup plus explicite sur ce qu'il faut implémenter (il est appliqué, pas simplement documenté) - mais dans la pratique, je n'ai pas trouvé que la définition de rappel soit correcte.

    Définir et implémenter une interface n'est pas si grave (à moins que vous ne dissortingbuiez des applets, comme si vous évitiez de créer des classes supplémentaires), mais c'est vraiment lorsque vous avez plusieurs rappels dans une même classe. Non seulement est-il obligé de les pousser chacun dans une classe interne distincte, mais il est carrément fastidieux de programmer et tout ce code chaud est simplement du "bruit".

    Bien qu’il soit loin d’être aussi propre, vous pouvez implémenter quelque chose comme les delegates C # en utilisant un proxy Java.

    Non, mais son comportement est similaire en interne.

    Dans C #, les delegates sont utilisés pour créer un point d’entrée distinct et fonctionnent comme un pointeur de fonction.

    En Java, il n’y a rien comme pointeur de fonction (sur un aspect supérieur), mais en interne, Java doit faire la même chose pour atteindre ces objectives.

    Par exemple, la création de threads en Java nécessite une classe étendant Thread ou implémentant Runnable, car une variable d’object de classe peut être utilisée comme pointeur d’emplacement mémoire.

    Oui et non, mais le modèle de délégué en Java peut être pensé de cette façon. Ce didacticiel vidéo traite de l’échange de données entre des fragments d’activité, et il présente une grande essence de modèle sorta de délégué utilisant des interfaces.

    Interface Java

    Java n’a pas de delegates et en est fier :). D’après ce que j’ai lu ici, j’ai trouvé essentiellement deux façons de simuler les delegates: 1. la reflection; 2. classe interne

    Les reflets sont slooooow! La classe interne ne couvre pas le cas d’utilisation le plus simple: la fonction de sorting. Vous ne voulez pas entrer dans les détails, mais la solution avec la classe interne consiste essentiellement à créer une classe wrapper pour un tableau d’entiers à sortinger dans l’ordre croissant et une classe pour un tableau d’entiers à classer dans l’ordre décroissant.