Visualizzazione dei risultati da 1 a 8 su 8
  1. #1

    Passare riferimento a funzioni come parametro

    Un saluto a tutti,
    sono nuovo del forum, e anche del mondo Java. Stavo studiando java convertendo del mio vecchio codice scritto in C. Mi sono fermato contro un muro che non riesco a superare, probabilmente perché non ho ancora ben chiaro tutte le possibilità che il Java mi offre.

    Ecco lo scenario:
    Ho una mia classe che crea una istanza di un'altra classe da me scritta, per esempio "MyWorld":

    codice:
    public class Classe1
    {
     public Classe1()
     {
      MyWorld w=new MyWorld();
      w.WriteAllMessage("qui qua qua");
     }
     public void WriteMessage(String text)
     {
      System.out.println("msg: "+text);
     }
    }
    
    public class MyWorld
    {
     public MyWorld() {}
     public void WriteAllMessage(String myText)
     {
        // Voglio chiamare il metodo "WriteMessage" di classe 1
     }
    }
    Il quesito mi sembra chiaro: nella funzione "WriteAllMessage" voglio richiamare il metodo di classe 1. Una possibile soluzione sarebbe quella di passare l'istanta di classe 1 a MyWorld, oppure MyClasse1 potrebbe esporre quel metodo come una mia interfaccia. Ma non esiste un modo come in C di passare come parametro per riferimento il nome di una funzione? Esiste in Java questa possibilità come è possibile anche in C# con i delegate?

    Grazie.

  2. #2
    Moderatore di Programmazione L'avatar di LeleFT
    Registrato dal
    Jun 2003
    Messaggi
    17,317
    Java non è C e non tutto quello che puoi fare in C può essere riscritto allo stesso modo in Java.
    Java è un linguaggio pienamente Object Oriented... non esiste il concetto di "funzione", quindi non esiste il concetto di "riferimento ad una funzione". Esistono solo gli oggetti ed i metodi che essi espongono.

    Hai due possibilità per risolvere:

    1) Passi un riferimento a Classe1 all'oggetto MyWorld in modo che quest'ultimo ne richiami il metodo
    2) Rendi pubblico e statico il metodo che vuoi usare in modo che MyWorld possa referenziarlo tramite Classe1.nomeMetodo().

    Chiaramente ci sono forti implicazioni (che non ha senso discutere in questa sede) in base all'approccio scelto.


    Ciao.
    "Perchè spendere anche solo 5 dollari per un S.O., quando posso averne uno gratis e spendere quei 5 dollari per 5 bottiglie di birra?" [Jon "maddog" Hall]
    Fatti non foste a viver come bruti, ma per seguir virtute e canoscenza

  3. #3
    Innanzitutto grazie per la risposta. Da quanto scrivi capisco che devo cambiare "approccio"

    Diciamo che nella mia esigenza avrei bisogno di utilizzare lo stesso meccanismo degli eventi. Sempre nel mio esempio sopra, la classe MyWorld dovrebbe poter avvisare la classe Classe1 richiamando un suo metodo. Più in grande, vorrei che MyWorld potesse avvisare anche eventualemente una Classe2, Classe3 e così via.

    Ora sto dando un'occhiata alla gestione degli eventi in Java (listner e compagnia bella). Può essermi utile?

    Grazia ancora.

  4. #4
    Moderatore di Programmazione L'avatar di LeleFT
    Registrato dal
    Jun 2003
    Messaggi
    17,317
    Originariamente inviato da MadeInItaly
    Ora sto dando un'occhiata alla gestione degli eventi in Java (listner e compagnia bella). Può essermi utile?
    Direi che è l'approccio corretto.
    L'implementazione è anche abbastanza semplice: la classe che genera eventi dovrebbe avere una lista di "listener" da notificare in cascata ogni volta che viene generato l'evento. La classe dovrebbe disporre di un metodo atto ad aggiungere un nuovo ascoltatore alla lista.


    Ciao.
    "Perchè spendere anche solo 5 dollari per un S.O., quando posso averne uno gratis e spendere quei 5 dollari per 5 bottiglie di birra?" [Jon "maddog" Hall]
    Fatti non foste a viver come bruti, ma per seguir virtute e canoscenza

  5. #5
    Allora proseguo su questa strada.

    Grazie e ciao

  6. #6
    Utente di HTML.it
    Registrato dal
    Aug 2002
    Messaggi
    8,013
    senza voler mettere troppa carne sul fuoco, oltre ai "listener" potresti voler dare un'occhiata al concetto/pattern observer/observable

    Però, se stai cominciando ora, al di là del fatto che tu non sia un neofita della programmazione in generale, ma solo di quella in java (o forse per tutto quanto OO), darei un'occhiata ai concetti di base della programmazione in java prima di addentrarmi in specifiche tipicamente da secondo o terzo giorno
    <´¯)(¯`¤._)(¯`»ANDREA«´¯)(_.¤´¯)(¯`>
    "The answer to your question is: welcome to tomorrow"

  7. #7
    Grazie anche a te Andrea per il suggerimento.

    Ho fatto delle prove con la classe Observable in Java.util e la cosa mi è utile. L'unico problema che ho riscontrato con questa tecnica è nella necessità di ereditare la classe che invierà le notifiche dall'oggetto Observable; nel mio caso non mi è possibile perché la classe in questione eredita già da un'altra classe. In ogni caso grazie per il suggerimento, ho imparato un'altra cosa che mi sarà utile

    Ciao

  8. #8
    Trascrivo qui quello che ho scoperto finora dalla mia esplorazione del mondo Java a riguardo la necessità che aveva riscontrato all'inizio di questo thread - utilizzare quello che nel mondo Microsoft .net viene chiamato delegate o in C è il passaggio del ref di una funzione come parametro.

    Io ho testato quattro metodi (ho tralasciato il metodo del passaggio dell'istanza di classe):
    • Reflection
    • Interface
    • Pattern Observer
    • Event con Listener


    Con la reflection il tutto si risolve con poche righe di codice:
    codice:
    import java.lang.reflect.Method;
    public class ClasseA {
    
        public void toConsole(String str) {
            System.out.println(str + " dalla ClasseA");
        }
    
        public void TestDelegate1() {
    
            Method m1;
            try {
                m1 = ClasseA.class.getMethod("toConsole",
                        new Class[]{String.class});
                ClasseB d11 = new ClasseB();
                d11.TestReflection(this, m1);
    
            } catch (NoSuchMethodException | SecurityException e) {
            }
    
        }
    }
    /******************************************/
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    
    public class ClasseB {
     public void TestReflection(Object instanceClass, Method method) {
            try {
                method.invoke(instanceClass, new Object[]{"Stringa da ClasseB"});
            } catch (IllegalAccessException |
                       IllegalArgumentException |
                       InvocationTargetException ex) {
            }
        }    
    }
    Richiamando la funzione TestDelegate1 in ClasseA vengono passati come parametro il riferimento a questa classe e al metodo toConsole al metodo della seconda classe che richiama il metodo passato con il parametro stringa voluto. Una volta lanciato si ottiene questo output:
    codice:
    Stringa da ClasseB dalla ClasseA
    Se posso dare un parere del tutto personale questa tecnica è molto personalizzabile e apre la strada a qualsiasi implementazione, ma di contro c'è la difficoltà nella lettura codice (e conseguente difficile manutenzione anche da utenti esterni) e fragilità di tutta la struttura perché come riferimento alla funzione si può passare qualsiasi metodo con signature differente e solo runtime si potrà vedere il conseguente errore.

    Con l'uso delle interfacce il tutto è più semplice:
    codice:
    interface IDelegate {
    
        void toConsole(String msg);
    }
    
    public class ClasseA implements IDelegate {
    
        @Override
        public void toConsole(String str) {
            System.out.println(str + " dalla ClasseA");
        }
    
        public void TestDelegate1() {
            ClasseB d11=new ClasseB();
            d11.TestInterface(this);
        }
        
    }
    /*********************************************/
    public class ClasseB {
    
        void TestInterface(ClasseA interfaccia) {
            interfaccia.toConsole("Stringa da Classe B");
        }
    }
    Come l'esempio sopra, quando viene richiamato il metodo TestDelegate1 viene visualizzato questo output:
    codice:
    Stringa da Classe B dalla ClasseA
    Qui il codice è molto più leggibile del precedente e l'uso delle interfacce non preclude l'ereditarietà da altre classi o l'implementazione di altre interfacce. Tra tutti i metodo qui esposti è probabilmente il metodo più semplice da implementare.

    Ecco il codice che utilizza la terza tecnica: il pattern observer:
    codice:
    import java.util.Observable;
    import java.util.Observer;
    
    public class ClasseA implements Observer {
    
        public void TestDelegate1() {
            ClasseB d11 = new ClasseB();
            d11.addObserver(this);
            d11.TestPattern();
        }
    
        @Override
        public void update(Observable o, Object arg) {
            System.out.println(arg.toString() + " dalla ClasseA");
        }
    }
    /*********************************************/
    import java.util.Observable;
    
    public class ClasseB extends Observable {
    
        public void TestPattern() {
            setChanged();
            notifyObservers("Stringa da Classe B");
        }
    }
    Per l'implementazione di questa tecnica ho utilizzato la classe Observable e l'interfaccia Observer presenti nel package Java.util. In questo caso la ClasseA che rimane in attesa deve ereditare dalla classe Observable, quindi deve essere aggiunta anche il metodo update che sarà richiamato nel caso di esempio qui sopra dall'altra classe. ClasseB, che deve richiamare il metodo, deve implementare l'interfaccia Observer e ci mette a disposizione nella nostra classe due metodi aggiuntivi:
    • setChanged()
    • notifyObservers(Object)

    Utilizzando come nell'esempio qui sopra possiamo passare un oggetto alla classe in attesa (nel mio esempio passo una banale stringa).

    Il vantaggio di questa tecnica è che possiamo attaccare anche più classi che saranno tutte richiamate dall'observer:
    codice:
    ClasseB d11 = new ClasseB();
    ClasseA a1 = new ClasseA();
    ClasseA a2 = new ClasseA();
    ClasseA a3 = new ClasseA();
    ClasseA a4 = new ClasseA();
    ClasseA a5 = new ClasseA();
    d11.addObserver(a1);
    d11.addObserver(a2);
    d11.addObserver(a3);
    d11.addObserver(a4);
    d11.addObserver(a5);
    Lo svantaggio di questo approccio è nella necessità di ereditare dalla classe Observable che preclude qualsiasi altra ereditarietà per questa classe. Se non si ha questa necessità questa è la tecnica più flessibile e personalmente la ritendo la migliore da utilizzare.

    L'ultimo metodo presa in considerazione è Event con Listener è probabilmente la più complessa da implementare ed è la classica tecnica che io definisco copia&incolla perché è impossibile da ricordare e implementare a memoria

    Per implementare questa tecnica si devono creare due nuove classi. La prima la si deve ereditare dalla classe EventObject:
    codice:
    import java.util.EventObject;
    
    public class MyEvent extends EventObject {
    
        String _message;
        
        public String getMessage()
        {
            return _message;
        }
        public MyEvent(Object source, String msg) {
            super(source);
            _message=msg;
        }
    }
    In questo caso ho aggiunto anche una funziona aggiuntiva per avere il contenuto del messaggio, stringa che dovrà transitare dalla classeB alla classeA.

    La seconda classe da implementare è per il Listener:
    codice:
    import java.util.EventListener;
    
    public interface MyEventListener extends EventListener {
        public void myEventOccurred(MyEvent evt);
    }
    In questo caso non mi serve nessuna personalizzazione e lascio tutto com'è. Infine ecco le due classi che utilizzano gli eventi per il messaggio:
    codice:
    public class ClasseA implements MyEventListener {
    
        public void TestDelegate1() {
            ClasseB d11 = new ClasseB();
            d11.addMyEventListener(this);
            d11.TestEvent();
        }
    
        @Override
        public void myEventOccurred(MyEvent evt) {
            System.out.println(evt.getMessage() + " dalla ClasseA");
        }
    }
    /*******************************************/
    public class ClasseB {
    
        protected javax.swing.event.EventListenerList listenerList =
                new javax.swing.event.EventListenerList();
    
        public void addMyEventListener(MyEventListener listener) {
            listenerList.add(MyEventListener.class, listener);
        }
    
        public void removeMyEventListener(MyEventListener listener) {
            listenerList.remove(MyEventListener.class, listener);
        }
    
        void fireMyEvent(MyEvent evt) {
            Object[] listeners = listenerList.getListenerList();
            for (int iii = 0; iii < listeners.length; iii += 2) {
                if (listeners[iii] == MyEventListener.class) {
                    ((MyEventListener) listeners[iii + 1]).myEventOccurred(evt);
                }
            }
        }
    
        public void TestEvent() {
            this.fireMyEvent(new MyEvent(this, "Stringa da Classe B"));
        }
    }
    Il risultato è quello ormai noto:
    codice:
    Stringa da Classe B dalla ClasseA
    Nella ClasseA non ho fatto altro che implementare l'interfaccia creata poco sopra MyEventListener e inserito il metodo che sarà richiamato quando sarà avviato l'evento: myEventOccurred. ClasseB ora deve implementare tutta la logica per la gestione dei Listener. Per la logica di funzionamento rimando ai link in calce a questo post.

    Questa tecnica non ha limiti e limitazioni visto che si devono avere solo due accortezze: implementare una interfaccia alla classe e la scrittura di funzioni personalizzate all'interno della seconda classe per la gestione dei Listener.

    Trascrivo qui tutto questo per avere una mia nota personale e per i neofiti che un giorno potrebbero riscontrare la stessa necessità. Naturalmente tutto quanto esposto non dev'essere inteso completo (non ho una conoscienza di Java così approfondita). Ogni critica costruttiva è ben accetta. Infine ringrazio ancora LeleFT e Andrea1979 per gli spunti dati.

    Link di riferimento da dove ho studiato/preso le tecniche esposte:

Permessi di invio

  • Non puoi inserire discussioni
  • Non puoi inserire repliche
  • Non puoi inserire allegati
  • Non puoi modificare i tuoi messaggi
  •  
Powered by vBulletin® Version 4.2.1
Copyright © 2025 vBulletin Solutions, Inc. All rights reserved.