Delegații Java? (Programare, Java, Delegați)

markus a intrebat.

Are limbajul Java caracteristici de delegare, similare cu modul în care C# are suport pentru delegați?

Comentarii

    22

  • @Suma Cum poate fi aceasta un duplicat dacă întrebarea pe care ați menționat-o a fost postată la un an după aceasta? –  > Por Ani.
  • Java 8 are o caracteristică destul de asemănătoare cu delegatele. Se numește lambdas. –  > Por tbodt.
  • @tbodt pentru a fi mai corect, Java 8 are o caracteristică asemănătoare cu cea a delegaților. Se numește interfețe funcționale. Lambdas sunt o modalitate de a crea astfel de instanțe delegate (anonim). –  > Por nawfal.
  • Răspunsurile de mai jos sunt din Pre-Java 8. Post Java 8, consultați răspunsurile din acest topic: stackoverflow.com/questions/20311779/… – –  > Por Michael Lloyd Lee mlk.
  • @nawfal, +1, dar pentru a fi și mai corect, Java 7 și următoarele au deja o caracteristică destul de asemănătoare cu delegates. Se numește interfețe simple. –  > Por Pacerier.
15 răspunsuri
Matt Sheppard

Nu chiar, nu.

Este posibil să puteți obține același efect folosind reflecția pentru a obține obiecte de Metodă pe care apoi să le puteți invoca, iar cealaltă modalitate este de a crea o interfață cu o singură metodă „invoke” sau „execute”, iar apoi să le instanți pentru a apela metoda care vă interesează (adică folosind o clasă interioară anonimă).

S-ar putea ca acest articol să vă pară interesant / util și pentru dumneavoastră : A Java Programmer Looks at C# Delegates (@blueskyprojects.com)

Comentarii

  • Soluția cu invoke() ca unică funcție membră a unei interfețe este foarte bună. –  > Por Stephane Rolland.
  • Dar vedeți exemplul meu de aici despre ce ar trebui să se facă, chiar și în Java 7, pentru a realiza echivalentul unui delegat C#. –  > Por ToolmakerSteve.
Dave Cousineau

În funcție de ce anume vreți să spuneți, puteți obține un efect similar (trecerea unei metode) folosind Strategy Pattern.

În loc de o linie ca aceasta care declară o semnătură de metodă cu nume:

// C#
public delegate void SomeFunction();

declară o interfață:

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

Pentru implementări concrete ale metodei, definiți o clasă care implementează comportamentul:

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

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

Apoi, oriunde ați fi avut un SomeFunction delegat în C#, utilizați un ISomeBehaviour referință în loc:

// C#
SomeFunction doSomething = SomeMethod;
doSomething();
doSomething = SomeOtherMethod;
doSomething();

// Java
ISomeBehaviour someBehaviour = new TypeABehaviour();
someBehaviour.SomeFunction();
someBehaviour = new TypeBBehaviour();
someBehaviour.SomeFunction();

Cu clasele interioare anonime, puteți chiar evita să declarați clase separate cu nume și să le tratați aproape ca pe adevărate funcții delegate.

// Java
public void SomeMethod(ISomeBehaviour pSomeBehaviour) {
   ...
}

...

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

Acest lucru ar trebui probabil utilizat doar atunci când implementarea este foarte specifică contextului curent și nu ar beneficia de reutilizare.

Și apoi, desigur, în Java 8, acestea devin practic expresii lambda:

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

Comentarii

  • +1. Aceasta este o soluție decentă, iar verbozitatea ar putea fi compensată în viitor prin mentenabilitate. –  > Por nawfal.
  • acest lucru este minunat… În prezent, lucrez la un proiect în care nu pot folosi reflecția din cauza constrângerilor proiectului și această soluție de rezolvare face treaba minunat 🙂 –  > Por Jonathan Camarena.
  • Are Java o convenție de denumire cu prefixul I pentru interfețe? Nu am mai văzut așa ceva până acum –  > Por Kyle Delaney.
Patrick

Pe scurt: nu.

Introducere

Cea mai nouă versiune a mediului de dezvoltare Microsoft Visual J++ suportă o construcție de limbaj numită delegates sau referințe de metode legate. Această construcție, precum și noile cuvinte cheie delegate șimulticast introduse pentru a o susține, nu fac parte din JavaTMcare este specificat de către Specificația limbajului Java și modificat prin Specificația privind clasele interne inclusă în documentația pentru software-ul JDKTM 1.1..

Este puțin probabil ca limbajul de programare Java să includă vreodată această construcție. Sun a luat deja în considerare cu atenție adoptarea acesteia în 1996, până la a construi și a renunța la prototipuri de lucru. Concluzia noastră a fost că referințele la metode legate sunt inutile și dăunătoare pentru limbaj. Această decizie a fost luată în urma consultării cu Borland International, care avea experiență anterioară cu referințe de metode legate în Delphi Object Pascal.

Considerăm că referințele de metode legate sunt inutile deoarece există o altă alternativă de proiectare, clasele interioare, oferă o funcționalitate egală sau superioară. În special, clasele interioare susțin pe deplin cerințele de gestionare a evenimentelor din interfața utilizatorului și au fost utilizate pentru a implementa o API pentru interfața utilizatorului cel puțin la fel de cuprinzătoare ca și clasele Windows Foundation.

Considerăm că referințele la metode legate sunt dăunătoare pentru că acestea afectează simplitatea limbajului de programare Java și caracterul orientat în mod omniprezent spre obiect al API-urilor. De asemenea, referințele la metode legate introduc neregularități în sintaxa limbajului și în regulile de acoperire. În cele din urmă, ele diluează investiția în tehnologiile VM, deoarece VM-urile sunt necesare pentru a gestiona în mod eficient tipuri suplimentare și disparate de referințe și legături între metode.

Comentarii

  • După cum se spune în ceea ce Patrick a legat doriți să folosiți în schimb clase interioare. –  > Por SCdF.
  • 18

  • Un articol excelent. IUBESC definiția lor de „simplu”: Java este conceput pentru a fi un limbaj simplu, ca în „Java trebuie să fie simplu de scris pentru compilator/VM”, ignorând în același timp „Java trebuie să fie simplu de scris/citit de către o persoană”. Explică multe. –  > Por Juozas Kontvainis.
  • Cred doar că SUN a făcut o mare greșeală. Pur și simplu nu au fost convinși de paradigma funcțională și atât. –  > Por Stephane Rolland.
  • @Juozas: Python este făcut în mod explicit pentru a fi simplu de scris/citit de către o persoană și implementează funcții lambda/delegate.. –  > Por Stephane Rolland.
  • Înlocuit cu un link către archive.org. De asemenea, este foarte prostesc, Oracle. –  > Por Patrick.
Michael

Ați citit acest :

Delegații sunt o construcție utilă în sistemele bazate pe evenimente. În esență, delegații sunt obiecte care codifică o expediere de metodă pe un obiect specificat. Acest document arată modul în care clasele interne java oferă o soluție mai generică la astfel de probleme.

Ce este un delegat? Într-adevăr, este foarte asemănător cu un pointer la o funcție membră, așa cum se utilizează în C++. Dar un delegat conține obiectul țintă împreună cu metoda care urmează să fie invocată. În mod ideal, ar fi frumos să putem spune:

obj.registerHandler(ano.methodOne);

…și că metoda methodOne ar fi apelată pe ano atunci când se primește un anumit eveniment.

Aceasta este ceea ce realizează structura Delegate.

Clasele interne Java

S-a afirmat că Java oferă această funcționalitate prin intermediul claselor interne anonime și, prin urmare, nu are nevoie de construcția suplimentară Delegate.

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

La prima vedere, acest lucru pare corect, dar, în același timp, este o pacoste. Deoarece pentru multe exemple de procesare a evenimentelor, simplitatea sintaxei Delegates este foarte atractivă.

Manipulator general

Cu toate acestea, dacă programarea bazată pe evenimente este utilizată într-un mod mai răspândit, de exemplu, ca parte a unui mediu general de programare asincronă, miza este mai mare.

Într-o astfel de situație generală, nu este suficientă includerea doar a metodei țintă și a instanței obiectului țintă. În general, pot fi necesari ș i alți parametri, care sunt determinați în contextul în care se înregistrează gestionarul de evenimente.

În această situație mai generală, abordarea java poate oferi o soluție foarte elegantă, în special atunci când este combinată cu utilizarea variabilelor finale:

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);
    }
  } );
}

final * final * final * final

Ați fost atenți?

Rețineți că variabilele finale sunt accesibile din interiorul definițiilor metodelor clasei anonime. Asigurați-vă că studiați cu atenție acest cod pentru a înțelege ramificațiile. Aceasta este potențial o tehnică foarte puternică. De exemplu, poate fi utilizată cu succes la înregistrarea gestionarilor în MiniDOM și în situații mai generale.

În schimb, construcția Delegate nu oferă o soluție pentru această cerință mai generală și, ca atare, ar trebui respinsă ca un idiom pe care se pot baza proiectele.

utilizator3311658

Știu că această postare este veche, dar Java 8 a adăugat lambdas, precum și conceptul de interfață funcțională, care este orice interfață cu o singură metodă. Împreună, acestea oferă o funcționalitate similară cu cea a delegaților C#. Vedeți aici pentru mai multe informații, sau pur și simplu căutați pe Google Java Lambdas.http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-final.html

Dominic Fox

Nu, dar se pot falsifica folosind proxy-uri și reflecție:

  public static class TestClass {
      public String 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<TestClass, Callable<String>> knockKnockDelegator = Delegator.ofMethod("knockKnock")
                                                                   .of(TestClass.class)
                                                                   .to(Callable.class);
      Callable<String> callable = knockKnockDelegator.delegateTo(testInstance);
      assertThat(callable.call(), is("who's there?"));
  }

Partea bună a acestui idiom este că puteți verifica dacă metoda delegat-la există și dacă are semnătura necesară în momentul în care creați delegatorul (deși nu la compilare, din păcate, deși un plug-in FindBugs ar putea ajuta aici), apoi îl puteți folosi în siguranță pentru a delega la diverse instanțe.

A se vedea codul karg pe github pentru mai multe teste și implementarea.

Khulja Sim Sim Sim

Da & Nu, dar modelul delegat în Java ar putea fi gândit în acest fel. Acest tutorial video este despre schimbul de date între activitate și fragmente și are o esență excelentă a modelului de tip delegat folosind interfețe.

Lawrence Dol

Am implementat suportul callback/delegate în Java folosind reflecția. Detaliile și sursa de lucru sunt disponibile pe site-ul meu.

Cum funcționează

Există o clasă principală numită Callback cu o clasă imbricata numită WithParms. API-ul care are nevoie de callback va lua un obiect Callback ca parametru și, dacă este necesar, va crea un Callback.WithParms ca variabilă de metodă. Deoarece o mare parte din aplicațiile acestui obiect vor fi recursive, acest lucru funcționează foarte curat.

Având în vedere că performanța este încă o prioritate pentru mine, nu am vrut să fiu nevoit să creez o matrice de obiecte de aruncat pentru a păstra parametrii pentru fiecare invocare – la urma urmei, într-o structură de date mare ar putea exista mii de elemente, iar într-un scenariu de procesare a mesajelor am putea ajunge să procesăm mii de structuri de date pe secundă.

Pentru a fi threadsafe, matricea de parametri trebuie să existe în mod unic pentru fiecare invocare a metodei API și, pentru eficiență, aceeași matrice trebuie utilizată pentru fiecare invocare a callback-ului; aveam nevoie de un al doilea obiect care să fie ieftin de creat pentru a lega callback-ul cu o matrice de parametri pentru invocare. Dar, în unele scenarii, cel care invocă ar trebui să aibă deja un array de parametri din alte motive. Din aceste două motive, matricea de parametri nu are ce căuta în obiectul Callback. De asemenea, alegerea modului de invocare (transmiterea parametrilor sub formă de matrice sau de obiecte individuale) este de competența API-ului care utilizează callback-ul, permițându-i acestuia să utilizeze orice invocare care se potrivește cel mai bine funcționării sale interne.

Prin urmare, clasa imbricata WithParms este opțională și are două scopuri: conține matricea de obiecte de parametri necesară pentru invocările callback-ului și oferă 10 metode supraîncărcate invoke() (cu 1 până la 10 parametri) care încarcă matricea de parametri și apoi invocă ținta callback-ului.

În continuare este prezentat un exemplu de utilizare a unui callback pentru a procesa fișierele dintr-un arbore de directoare. Aceasta este o primă trecere de validare care doar numără fișierele care trebuie procesate și se asigură că niciunul nu depășește o dimensiune maximă predeterminată. În acest caz, creăm pur și simplu callback-ul în linie cu invocarea API. Cu toate acestea, reflectăm metoda țintă ca o valoare statică, astfel încât reflectarea să nu fie efectuată de fiecare dată.

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;
 * <p>
 * The callback signature is:
 * <pre>    void callback(File dir, File ent);</pre>
 * <p>
 * @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<lst.length; xa++) {
                File ent=lst[xa];
                if(!ent.isDirectory()) {
                    cbk.invoke(dir,ent);
                    lst[xa]=null;
                    cnt++;
                    }
                }
            for(int xa=0; xa<lst.length; xa++) {
                File ent=lst[xa];
                if(ent!=null) { cnt+=_processDirectory(ent,cbk,sel); }
                }
            }
        }
    return cnt;
    }

Acest exemplu ilustrează frumusețea acestei abordări – logica specifică a aplicației este abstractizată în callback, iar corvoada de a parcurge recursiv un arbore de directoare este bine ascunsă într-o metodă utilitară statică complet reutilizabilă. Și nu trebuie să plătim în mod repetat prețul definirii și implementării unei interfețe pentru fiecare nouă utilizare. Bineînțeles, argumentul pentru o interfață este că este mult mai explicit cu privire la ce trebuie implementat (este impus, nu doar documentat) – dar în practică nu am constatat că este o problemă să se definească corect definiția callback-ului.

Definirea și implementarea unei interfețe nu este chiar atât de rău (cu excepția cazului în care distribuiți applet-uri, așa cum fac eu, unde evitarea creării de clase suplimentare chiar contează), dar unde acest lucru strălucește cu adevărat este atunci când aveți mai multe callback-uri într-o singură clasă. Nu numai că a fi forțat să le împingi pe fiecare într-o clasă interioară separată adaugă costuri suplimentare în aplicația implementată, dar este de-a dreptul plictisitor să programezi și tot acel cod de tip boiler-plate este de fapt doar „zgomot”.

53c

Nu are o clasă explicită delegate explicit ca în C#, dar puteți obține un rezultat similar în Java 8 folosind o interfață funcțională (adică orice interfață cu exact o metodă) și lambda:

private interface SingleFunc {
    void printMe();
}

public static void main(String[] args) {
    SingleFunc sf = () -> {
        System.out.println("Hello, I am a simple single func.");
    };
    SingleFunc sfComplex = () -> {
        System.out.println("Hello, I am a COMPLEX single func.");
    };
    delegate(sf);
    delegate(sfComplex);
}

private static void delegate(SingleFunc f) {
    f.printMe();
}

Fiecare obiect nou de tip SingleFunc trebuie să implementeze printMe(), , astfel încât este sigur să fie trecut la o altă metodă (de ex. delegate(SingleFunc)) pentru a apela metoda printMe() metodă.

Hervian

Cu safety-mirror pe classpath veți obține ceva similar cu delegații și evenimentele din C#.

Exemple din README al proiectului:

Delegații în Java!

  Delegate.With1Param<String, String> greetingsDelegate = new Delegate.With1Param<>();
  greetingsDelegate.add(str -> "Hello " + str);
  greetingsDelegate.add(str -> "Goodbye " + str);

  DelegateInvocationResult<String> invocationResult = 
  greetingsDelegate.invokeAndAggregateExceptions("Sir");

  invocationResult.getFunctionInvocationResults().forEach(funInvRes -> 

  System.out.println(funInvRes.getResult()));
  //prints: "Hello sir" and "Goodbye Sir"

Evenimente

  //Create a private Delegate. Make sure it is private so only *you* can invoke it.
  private static Delegate.With0Params<String> trimDelegate = new Delegate.With0Params<>();

  //Create a public Event using the delegate you just created.
  public static Event.With0Params<String> trimEvent= new Event.With0Params<>(trimDelegate)

Vedeți și acest răspuns SO.

John Meagher

Deși nu este nici pe departe la fel de curat, dar ați putea implementa ceva de genul delegaților C# folosind un Java Proxy.

Comentarii

  • În timp ce acest link poate răspunde la întrebare, este mai bine să includeți părțile esențiale ale răspunsului aici și să furnizați link-ul pentru referință. Răspunsurile bazate doar pe linkuri pot deveni invalide dacă se schimbă pagina legată. –  > Por StackFlowed.
  • @StackFlowed Dezbracă link-ul și ce obții? O postare cu informații. Votați pentru păstrare. –  > Por Scimonster.
  • @Scimonster ce s-ar întâmpla dacă link-ul nu mai este valabil ? –  > Por StackFlowed.
  • @StackFlowed Încă oferă un răspuns – folosiți un proxy Java. –  > Por Scimonster.
  • atunci ar putea fi lăsat ca un comentariu? –  > Por StackFlowed.
Manasvi Sareen

Nu, dar are un comportament similar, la nivel intern.

În C#, delegații sunt utilizați pentru a crea un punct de intrare separat și funcționează la fel ca un pointer de funcție.

În java nu există un lucru precum pointerul de funcție (la o privire superioară), dar, la nivel intern, Java trebuie să facă același lucru pentru a atinge aceste obiective.

De exemplu, crearea de fire de execuție în Java necesită o clasă care să extindă Thread sau să implementeze Runnable, deoarece o variabilă de obiect de clasă poate fi utilizată ca un pointer de locație de memorie.

J.Smile

Nu, Java nu are această caracteristică uimitoare. Dar ați putea să o creați manual folosind modelul observator. Iată un exemplu: Scrieți un delegat C# în java

DeV

Codul descris oferă multe dintre avantajele delegaților C#. Metodele, fie ele statice sau dinamice, pot fi tratate într-un mod uniform. Complexitatea apelării metodelor prin reflecție este redusă, iar codul este reutilizabil, în sensul că nu necesită clase suplimentare în codul utilizatorului. Rețineți că apelăm o versiune alternativă de conveniență a invoke, în care o metodă cu un singur parametru poate fi apelată fără a crea un array de obiecte. codul Java de mai jos:

  class Class1 {
        public void show(String s) { System.out.println(s); }
    }

    class Class2 {
        public void display(String s) { System.out.println(s); }
    }

    // allows static method as well
    class Class3 {
        public static void staticDisplay(String s) { System.out.println(s); }
    }

    public class TestDelegate  {
        public static final Class[] OUTPUT_ARGS = { String.class };
        public final Delegator DO_SHOW = new Delegator(OUTPUT_ARGS,Void.TYPE);

        public void main(String[] args)  {
            Delegate[] items = new Delegate[3];

            items[0] = DO_SHOW .build(new Class1(),"show,);
            items[1] = DO_SHOW.build (new Class2(),"display");
            items[2] = DO_SHOW.build(Class3.class, "staticDisplay");

            for(int i = 0; i < items.length; i++) {
                items[i].invoke("Hello World");
            }
        }
    }

Leve

Java nu are delegați și este mândru de asta :). Din ceea ce am citit aici am găsit în esență 2 modalități de a falsifica delegații:1. reflection;2. inner class

Reflecțiile sunt leneșe! Clasa interioară nu acoperă cel mai simplu caz de utilizare: funcția de sortare. Nu vreau să intru în detalii, dar soluția cu inner class practic este de a crea o clasă wrapper pentru un array de numere întregi care să fie sortat în ordine crescătoare și o clasă pentru un array de numere întregi care să fie sortat în ordine descrescătoare.

Comentarii

  • Ce legătură are clasa interioară cu sortarea? Și ce legătură are sortarea cu întrebarea? –  > Por nawfal.