Eseguire una classe a Runtime con Java Reflection

DOMANDA:

Sto creando un programma che ha bisogno di lanciare dei metodi di altre classi Java a runtime. Come posso fare?


RISPOSTA:

L'idea che mi viene in mente è Java Reflection, una caratteristica potentissima di Java che permette di ottenere a runtime molte informazioni sulle classi (metodi e parametri, costruttori, attributi, ecc..., ecc...).

Vediamo con l'esempio TestReflection come ispezionare una classe:

package reflection;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class TestReflection {
   public static void main(String[] args) throws ClassNotFoundException {

      // la classe da analizzare
      String classeDaInvocare = "java.lang.String";
      Class classeInvocata = Class.forName(classeDaInvocare);
   
      // elenco dei parametri dei vari costruttori
      Constructor[] costruttori = classeInvocata.getConstructors();
      for(Constructor c: costruttori){
         Class[] parametri = c.getParameterTypes();
         System.out.println("LISTA DEI PARAMETRI DEL COSTRUTTORE:");
    
         for(Class par: parametri)
             System.out.println("- " +par.getName());
      }
      // elenco dei metodi
      Method[] metodi = classeInvocata.getMethods();
      for(Method m: metodi)
         System.out.println("Metodo: " +m.getName());
   }
}

E' chiaro che saremmo potuti anche scendere ulteriormente nel dettaglio ispezionando i tipi di parametri per ogni metodo, la lista degli attributi, la visibilità, i tipi di dati restituiti, e tantissime altre informazioni, ma lascio al lettore l'approfondimento.

Una volta visto come ispezionare l'interno di una classe, rispondo alla domanda: "come faccio ad instanziare una classe e ad invocarne i metodi a Runtime?".

Vi mostro una classe di esempio (con un solo metodo) che andremo poi ad instanziare ed eseguire a runtime:

package reflection;

import java.io.File;

public class RinominaFile {

   private String pathFrom;
   private String newName;

   public RinominaFile(String pathFrom, String newName) {
      this.pathFrom = pathFrom;
      this.newName = newName;
   }

   public void rinomina() {
      File fromFile = new File(pathFrom);
      fromFile.renameTo(new File(newName));
   }
}

Questa classe semplicemente rinomina un file dal nome pathFrom a newName.


Ecco invece come si presenta la classe TestReflectionExec che, tramite la Java Reflection, invocherà il suo metodo rinomina():

package reflection;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class TestReflectionExec {
   public static void main(String[] args) 
                throws ClassNotFoundException, SecurityException, 
                NoSuchMethodException, IllegalArgumentException, 
                InstantiationException, IllegalAccessException, 
                InvocationTargetException {

      Class classeInvocata = Class.forName("reflection.RinominaFile");

      // creiamo la lista dei tipi di parametri
      Class listaParametri[] = { Class.forName("java.lang.String"), Class.forName("java.lang.String") };
      // e la lista dei corrispondenti valori
      Object[] listaValoriParametri = { "C:\\ciao.txt", "C:\\ciao1.txt" };

      // prendiamo il costruttore con quella lista di parametri
      Constructor costruttore = classeInvocata.getConstructor(listaParametri);
   
      // instanziamo la classe
      RinominaFile rf = (RinominaFile) costruttore.newInstance(listaValoriParametri);
   
      // ed invochiamo il metodo rinomina()
      classeInvocata.getMethod("rinomina").invoke(rf);
   }
}

n.b.: prima di eseguire il main assicuratevi di aver creato in "C:\" il file "ciao.txt", altrimenti non verrà rinominato nulla!

Una volta creato il file, eseguiamo la classe e, nella cartella "C:\", troveremo il file correttamente rinominato in "ciao1.txt".


In questo caso abbiamo creato una classe specifica per richiamare RinominaFile ed inserito tutti i parametri e i nomi all'interno del codice. In una realtà produttiva avrebbe poco senso, ma quei valori possono essere facilmente sostituiti con parametri riempiti dai più disparati input (tastiera, file, xml, stream, ecc..., ecc...). Come potete immaginare, con la Reflection avete praticamente infinite possibilità di applicazione.


Un esempio molto carino, che potrebbe essere sviluppato grazie a Java Reflection, è un programma che a runtime permette di scrivere codice Java, compilarlo e poi di instanziare le classi ed invocarne i metodi stessi (vi fa venire in mente nulla? :o).


Commenti

  1. L'esempio molto carino di cui parli alla fine dell'articolo è proprio quello che volevo fare in questi giorni, mi puoi suggerire qualche link o parola chiave da cui partire? Cosa intendi con "vi fa venire in mente nulla?"

    RispondiElimina
    Risposte
    1. Ciao Simone,
      grazie innanzitutto. La domanda finale stimola un po' la fantasia del lettore :o). In realtà le risposte possono essere più di una, probabilmente quella più veloce da dare potrebbe essere la seguente: una sorta di ambiente di programmazione con un debugger primitivo.
      Spero di averti risposto.

      Elimina

Posta un commento

Post popolari in questo blog

Arrotondamento e troncamento in Java

Eclipse: Shortcuts (scorciatoie) da tastiera

Strutture dati: List, Set, Map

Creare un eseguibile Java