Java Reflection: accesso a metodi e campi privati

DOMANDA:

Come si può accedere da un classe generica ai metodi e alle variabili di istanza private di un'altra classe?


RISPOSTA:

Genericamente non è possibile accedere ai campi o metodi privati di un'altra classe. Ma genericamente non vuol dire in modo assoluto! Infatti, grazie alla Reflection Java possiamo accedere ai campi privati di un'altra classe.

Analizziamo alcuni metodi che ci mette a disposizione la classe java.lang.Class:
  • getFields(): permette di recuperare la lista di tutti i campi pubblici della classe e della sua superclasse;
  • getDeclaredFields(): fornisce la lista di tutti i campi presenti nella classe (senza distinzione di visibilità);
  • getDeclaredMethods(): come sopra ma applicato ai metodi;
  • getDeclaredField(String nome): restituisce il Field indicato come parametro (senza distinzione di visibilità);
  • getDeclaredMethos(String nome): restituisce il Method indicato come parametro (senza distinzione di visibilità).

Guardiamo l'esempio, prima con la ClassePrivata da analizzare:
package reflection;

public class ClassePrivata {
  public int publicValue = 99999;
  protected int protectedValue = 200;
  int packageProtectedValue = 10000;
  private int secretValue = 12345;

  public String getPublicValue(){
     return "Il valore pubblico è: " +publicValue;
  }

  protected String getProtectedValue(){
     return "Il valore protected è: " +protectedValue;
  }

  String getPackageProtectedValue(){
     return "Il valore packageProtected è: "+packageProtectedValue;
  }

  private String getSecretValue(){
     return "Il valore segreto è: " +secretValue;
  }
}
E poi con la classe analizzatrice ElencoCampiMetodi:
package reflection;

import java.util.Arrays;

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

    Class<ClassePrivata> pvtClass = 
         (Class<ClassePrivata>) Class.forName("reflection.ClassePrivata");

    //getFields() non restituisce i campi privati
    System.out.println("Fields: " + 
                   Arrays.toString(pvtClass.getFields()));

    //getDeclaredFields() restituisce sia i campi privati che non
    System.out.println("Declared Fields: " +
                   Arrays.toString(pvtClass.getDeclaredFields()));    

    //getDeclaredMethods() restituisce sia i metodi privati che non
    System.out.println("Declared methods: " +
                   Arrays.toString(pvtClass.getDeclaredMethods()));
  }
}
L'output, come previsto, sarà:
Fields: [public int reflection.ClassePrivata.publicValue]
Declared Fields: [public int reflection.ClassePrivata.publicValue, int reflection.ClassePrivata.packageProtectedValue, private int reflection.ClassePrivata.secretValue]
Declared methods: [private java.lang.String reflection.ClassePrivata.getSecretValue(), public java.lang.String reflection.ClassePrivata.getPublicValue()]


Passiamo ora al recupero vero e proprio del valore di un campo privato e all'esecuzione di un metodo privato:
public class PrivateAccessTest {
  public static void main(String args[]) throws ClassNotFoundException {

    Class<ClassePrivata> pvtClass = 
         (Class<ClassePrivata>) Class.forName("reflection.ClassePrivata");

    try {
      ClassePrivata myClassPvt = new ClassePrivata();

      // recupero il campo privato e lo rendo accessibile
      Field campoPrivato = pvtClass.getDeclaredField("secretValue");
      campoPrivato.setAccessible(true);

      // recupero il valore del campo privato
      int valore =  campoPrivato.getInt(myClassPvt);   

      // stampo il campo privato e il suo valore
      System.out.println("Campo Privato: " + campoPrivato);
      System.out.println("Valore campo privato: " + valore);

      // recupero il metodo privato e lo rendo accessibile
      Method metodoPrivato = pvtClass.getDeclaredMethod("getSecretValue");
      metodoPrivato.setAccessible(true);

      // recupero il valore di ritorno del metodo privato
      String result = (String) metodoPrivato.invoke(myClassPvt);
      System.out.println(result);

    } catch (IllegalAccessException | IllegalArgumentException
           | InvocationTargetException | NoSuchFieldException
           | SecurityException | NoSuchMethodException e) {
      e.printStackTrace();
    }
  }
}
L'output anche in questo caso ci darà conferma del risultato voluto:
Campo Privato: private int reflection.ClassePrivata.secretValue
Valore campo privato: 12345
Il valore segreto è: 12345

NOTA: per accedere ad un campo o metodo privato dobbiamo renderlo accessibile grazie al metodo setAccessible(true). Se non lo facessimo, appena tentiamo l'accesso al campo o al metodo ci ritroveremo un'eccezione di questo tipo:

java.lang.IllegalAccessException:
    Class reflection.PrivateAccessTest can not access a member of class reflection.ClassePrivata with modifiers "private"

NOTA FINALE: il codice utilizza il multicatch introdotto da Java 7. Se state utilizzando una versione precedente di Java dovete sostituirlo con il catch alla vecchia maniera! Per ulteriori informazioni sul multicatch vi invito a leggere questo post.

Commenti

Post popolari in questo blog

Arrotondamento e troncamento in Java

Eclipse: Shortcuts (scorciatoie) da tastiera

Strutture dati: List, Set, Map

Creare un eseguibile Java