Domanda

Il linguaggio Java dispone di funzionalità di delegato, simili a come C# supporta i delegati?

È stato utile?

Soluzione

Non proprio no.

Potresti essere in grado di ottenere lo stesso effetto utilizzando la riflessione per ottenere oggetti Metodo che puoi quindi invocare e l'altro modo è creare un'interfaccia con un singolo metodo "invoca" o "esegui" e quindi istanziarli per chiamare il metodo sei interessato a (es.utilizzando una classe interna anonima).

Potresti trovare interessante/utile anche questo articolo: Un programmatore Java esamina i delegati C#

Altri suggerimenti

A seconda di cosa intendi esattamente, puoi ottenere un effetto simile (passando un metodo) utilizzando il modello di strategia.

Invece di una riga come questa che dichiara la firma di un metodo con nome:

// C#
public delegate void SomeFunction();

dichiarare un'interfaccia:

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

Per implementazioni concrete del metodo, definire una classe che implementa il comportamento:

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

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

Allora ovunque avresti avuto un SomeFunction delegato in C#, utilizzare un ISomeBehaviour riferimento invece:

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

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

Con le classi interne anonime, puoi anche evitare di dichiarare classi con nome separate e trattarle quasi come vere e proprie funzioni delegate.

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

...

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

Probabilmente dovrebbe essere utilizzato solo quando l'implementazione è molto specifica per il contesto attuale e non trarrebbe vantaggio dal riutilizzo.

E poi, ovviamente, in Java 8, queste diventano fondamentalmente espressioni lambda:

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

Storia breve:no.

introduzione

La versione più recente dell'ambiente di sviluppo di Microsoft Visual J ++ supporta un costrutto linguistico chiamato delegati O Riferimenti del metodo legato.Questo costrutto e le nuove parole chiave delegate E multicast introdotti per supportarlo, non fanno parte di JavaTMlinguaggio di programmazione, che è specificato dal Specifica della lingua Java e modificato dal Specifica delle classi interne incluso nel documentazione per il software JDKTM 1.1.

È improbabile che il linguaggio di programmazione Java includa mai questo costrutto.Sun già considerato attentamente l'adozione nel 1996, nella misura in cui la costruzione e lo scarto di prototipi di lavoro.La nostra conclusione è stata che i riferimenti al metodo vincolano non sono necessari e dannosi per il linguaggio.Questa decisione è stata presa in consultazione con Borland International, che ha avuto precedenti esperienze con riferimenti al metodo vincolato in Object Pascal.

Riteniamo che i riferimenti al metodo vincolato lo siano non necessario Perché un'altra alternativa di design, classi interiori, fornisce funzionalità uguali o superiori.In particolare, le classi interne supportano pienamente i requisiti della gestione degli eventi dell'interfaccia utente e sono state utilizzate per implementare un'API dell'interfaccia utente almeno completa delle classi di Fondazione Windows.

Riteniamo che i riferimenti al metodo vincolato lo siano dannoso Perché sminuiscono la semplicità del linguaggio di programmazione Java e il carattere pervasivamente orientato agli oggetti delle API.I riferimenti del metodo vincolato introducono anche irregolarità nelle regole di sintassi e scoping del linguaggio.Infine, diluiscono l'investimento nelle tecnologie VM perché le VM sono necessarie per gestire i tipi aggiuntivi e disparati di riferimenti e il collegamento del metodo in modo efficiente.

Hai letto Questo :

I delegati sono un costrutto utile nei sistemi basati sugli eventi.Essenzialmente i delegati sono oggetti che codificano una spedizione del metodo su un oggetto specificato.Questo documento mostra come le classi interne Java forniscano una soluzione più generica a tali problemi.

Cos'è un delegato?In realtà è molto simile a un puntatore alla funzione membro utilizzato in C ++.Ma un delegato contiene l'oggetto target insieme al metodo da invocare.Idealmente sarebbe bello poter dire:

obj.registerHandler(ano.methodOne);

..e che il metodo MethodOne verrebbe chiamato ogni volta che veniva ricevuto un evento specifico.

Questo è ciò che ottiene la struttura dei delegati.

Classi interne Java

È stato sostenuto che Java fornisce questa funzionalità tramite classi interne anonime e quindi non ha bisogno del costrutto delegato aggiuntivo.

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

A prima vista questo sembra corretto ma allo stesso tempo fastidioso.Perché per molti esempi di elaborazione degli eventi la semplicità della sintassi dei delegati è molto interessante.

Gestore generale

Tuttavia, se la programmazione basata su eventi viene utilizzata in modo più pervasivo, ad esempio, come parte di un ambiente di programmazione asincrono generale, esiste molto di più.

In una situazione così generale, non è sufficiente includere solo il metodo target e l'istanza dell'oggetto target.In generale potrebbero esserci altri parametri necessari, che sono determinati nel contesto quando il gestore degli eventi è registrato.

In questa situazione più generale, l'approccio Java può fornire una soluzione molto elegante, in particolare se combinato con l'uso di variabili finali:

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

Hai attirato la tua attenzione?

Si noti che le variabili finali sono accessibili dalle definizioni del metodo della classe anonima.Assicurati di studiare attentamente questo codice per comprendere le ramificazioni.Questa è potenzialmente una tecnica molto potente.Ad esempio, può essere utilizzato a buon effetto quando si registrano gestori in minidom e in situazioni più generali.

Al contrario, il costrutto delegato non fornisce una soluzione per questo requisito più generale e come tale dovrebbe essere respinto come idioma su cui si possono basare i progetti.

So che questo post è vecchio, ma Java 8 ha aggiunto lambda e il concetto di interfaccia funzionale, ovvero qualsiasi interfaccia con un solo metodo.Insieme offrono funzionalità simili ai delegati C#.Vedi qui per maggiori informazioni o semplicemente google Java Lambdas.http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-final.html

No, ma sono falsificabili utilizzando proxy e riflessione:

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

La cosa bella di questo linguaggio è che puoi verificare che il metodo delegato esista e abbia la firma richiesta nel punto in cui crei il delegante (anche se non in fase di compilazione, sfortunatamente, anche se un plug-in FindBugs potrebbe help qui), quindi utilizzarlo in modo sicuro per delegare a varie istanze.

Vedi il codice karg su github per più test E implementazione.

Ho implementato il supporto callback/delegato in Java utilizzando la riflessione.I dettagli e la fonte di lavoro sono disponibile sul mio sito web.

Come funziona

Esiste una classe principale denominata Callback con una classe nidificata denominata WithParms.L'API che necessita del callback prenderà un oggetto Callback come parametro e, se necessario, creerà un Callback.WithParms come variabile del metodo.Poiché la maggior parte delle applicazioni di questo oggetto saranno ricorsive, il funzionamento è molto pulito.

Dato che le prestazioni sono ancora una priorità per me, non volevo essere obbligato a creare un array di oggetti usa e getta per contenere i parametri per ogni invocazione - dopotutto in una grande struttura dati potrebbero esserci migliaia di elementi, e in un'elaborazione di messaggi scenario potremmo finire per elaborare migliaia di strutture dati al secondo.

Per essere threadsafe, l'array di parametri deve esistere in modo univoco per ogni invocazione del metodo API e per efficienza lo stesso dovrebbe essere utilizzato per ogni invocazione del callback;Avevo bisogno di un secondo oggetto che fosse economico da creare per associare il callback con un array di parametri per l'invocazione.Ma, in alcuni scenari, l'invocatore avrebbe già un array di parametri per altri motivi.Per questi due motivi, l'array di parametri non appartiene all'oggetto Callback.Anche la scelta dell'invocazione (passando i parametri come array o come singoli oggetti) è nelle mani dell'API che utilizza il callback che le consente di utilizzare l'invocazione più adatta al suo funzionamento interno.

La classe nidificata WithParms, quindi, è opzionale e ha due scopi: contiene l'array di oggetti parametrici necessario per le invocazioni di callback e fornisce 10 metodi invoke() sovraccarichi (con da 1 a 10 parametri) che caricano l'array di parametri e quindi richiamare la destinazione della richiamata.

Quello che segue è un esempio che utilizza un callback per elaborare i file in un albero di directory.Si tratta di un passaggio di convalida iniziale che conta semplicemente i file da elaborare e garantisce che nessuno superi una dimensione massima predeterminata.In questo caso creiamo semplicemente il callback in linea con l'invocazione dell'API.Tuttavia, riflettiamo il metodo di destinazione come valore statico in modo che la riflessione non venga eseguita ogni volta.

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

Questo esempio illustra la bellezza di questo approccio: la logica specifica dell'applicazione viene astratta nel callback e la fatica di percorrere ricorsivamente un albero di directory è ben nascosta in un metodo di utilità statica completamente riutilizzabile.E non dobbiamo pagare ripetutamente il prezzo della definizione e dell'implementazione di un'interfaccia per ogni nuovo utilizzo.Naturalmente, l'argomento per un'interfaccia è molto più esplicita su cosa implementare (è applicata, non semplicemente documentata) - ma in pratica non ho trovato un problema ottenere la giusta definizione di callback.

Definire e implementare un'interfaccia non è poi così male (a meno che tu non stia distribuendo applet, come me, dove evitare di creare classi extra conta davvero), ma dove questo brilla davvero è quando hai più callback in una singola classe.Non solo è costretto a inserirli ciascuno in una classe interna separata che aggiunge un sovraccarico all'applicazione distribuita, ma è decisamente noioso da programmare e tutto quel codice standard è in realtà solo "rumore".

Anche se non è altrettanto pulito, ma potresti implementare qualcosa come i delegati C# usando Java Procura.

No, ma ha un comportamento simile, internamente.

In C# i delegati vengono utilizzati per creare un punto di ingresso separato e funzionano in modo molto simile a un puntatore a funzione.

In Java non esiste un puntatore a funzione (in alto) ma internamente Java deve fare la stessa cosa per raggiungere questi obiettivi.

Ad esempio, la creazione di thread in Java richiede una classe che estenda Thread o implementi Runnable, poiché una variabile oggetto di classe può essere utilizzata come puntatore di posizione di memoria.

Sì e no, ma il modello delegato in Java potrebbe essere pensato in questo modo. Questo video tutorial riguarda lo scambio di dati tra frammenti di attività e ha una grande essenza di modello di sorta di delegato che utilizza le interfacce.

Java Interface

Java non ha delegati e ne è orgoglioso :).Da quello che ho letto qui ho trovato in sostanza 2 modi per falsificare i delegati:1.riflessione;2.classe interiore

I riflessi sono lentiooooo!La classe interna non copre il caso d'uso più semplice:funzione di ordinamento.Non voglio entrare nei dettagli, ma la soluzione con la classe interna fondamentalmente è creare una classe wrapper per un array di numeri interi da ordinare in ordine crescente e una classe per un array di numeri interi da ordinare in ordine decrescente.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top