2013-05-19 11 views
5

Molto Recentemente ho trovato l'API Reflection e con mia sorpresa si può accedere e persino alterare la variables.I privata provato il seguente codiceL'API Dosen't Reflection interrompe lo scopo stesso dell'incapsulamento dei dati?

import java.lang.reflect.Field; 

public class SomeClass{ 
    private String name = "John"; 
} 

public class Test{ 
    public static void main(String args[]) throws Exception { 
     SomeClass myClass = new SomeClass(); 

     Field fs = myClass.getClass().getDeclaredField("name"); 
     fs.setAccessible(true); 

     System.out.println("Variable is " + fs.getName() + " and value is " 
       + fs.get(myClass)); 

     fs.set(myClass, "Sam"); 
     System.out.println("Variable is " + fs.getName() + " and value is " 
       + fs.get(myClass)); 
    } 
} 

e ho ottenuto il seguente output.

Variable is name and value is John 
Variable is name and value is Sam 

Diciamo Java è un linguaggio orientato agli oggetti ed è principale caratteristiche sono dati incapsulamento, ereditarietà, polimorfismo ecc .. Non è l'API di riflessione che cambia lo scopo di incapsulamento dei dati? Perché dobbiamo usare l'API di Reflection? Ho letto in alcuni siti che possono essere utilizzati a scopo di test, ma secondo me i moduli sono testati e possono essere fatti facilmente usando i casi di test JUnit. Quindi qualcuno può spiegare perché abbiamo un tale trucco?

+1

Una delle utilità dell'API di Reflection è il modello IOC, che consente al framework di chiamare metodi che sono sconosciuti al momento della compilazione (del framework).Altro uso è l'ispezione di clases per eseguire trasformazioni (vg, generazione di risposte SOAP o JSON direttamente dai bean) – SJuan76

+0

Nel codice non è necessario utilizzare 'fs.setAccessible (true);' perché si modifica il campo privato all'interno di un altro metodo di la stessa classe (che ha accesso a tutti i campi, anche a quelli privati). Ho modificato un po 'il tuo esempio per mostrare che con la riflessione puoi accedere a campi privati ​​in luoghi dove normalmente non sarebbe possibile. – Pshemo

risposta

9

Non è l'API di riflessione che modifica lo scopo stesso dell'incapsulamento dei dati?

Sì e no.

  • Sì, alcuni usi della riflessione API può dati rottura incapsulamento.
  • No, non tutti gli usi dell'API di riflessione do incapsulano dati di interruzione. In effetti, un programmatore saggio interrompe solo l'incapsulamento tramite l'API di reflection quando c'è una buona ragione per farlo.
  • No, l'API di reflection non modifica lo scopo di incapsulamento dei dati. Lo scopo di incapsulamento dei dati rimane lo stesso ... anche se qualcuno lo interrompe volontariamente.

Perché dobbiamo utilizzare l'API di Reflection?

Ci sono molti usi di riflessione che Non pausa incapsulamento; per esempio. usando la riflessione per scoprire che tipo super ha una classe, quali annotazioni ha, quali membri ha, per invocare metodi e costruttori accessibili, leggere e aggiornare campi accessibili e così via.

E ci sono situazioni in cui è accettabile (a vari livelli) per utilizzare l'incapsulamento rottura varietà di riflessione:

  • si potrebbe aver bisogno di guardare dentro un tipo incapsulato (ad esempio, l'accesso/modificare i campi privati) come il modo più semplice (o unico) per implementare determinati test unitari.

  • Alcune forme di Dipendenza Iniezione (aka IoC), Serializzazione e Persistenza implicano l'accesso e/o l'aggiornamento di campi privati.

  • Molto raramente, è necessario interrompere l'incapsulamento per aggirare un bug in una classe che non è possibile correggere.

ho letto in alcuni siti che può essere utilizzato a scopo di test, ma secondo me i moduli sono testati e che può essere fatto facilmente usando casi di test JUnit. Quindi qualcuno può spiegare perché abbiamo un tale trucco?

Dipende dal design della classe. Una classe progettata per essere testabile può essere testabile senza la necessità di accedere allo stato "privato" o esporre tale stato (ad esempio protected getter) per consentire il test. Se la classe non fa questo, allora un test di JUnit potrebbe aver bisogno di usare il riflesso per guardare all'interno dell'astrazione.

questo non è auspicabile (IMO), ma se si sta scrivendo unit test per una classe che qualcuno ha scritto, e non si può "modificare" le API per migliorare la verificabilità, allora potrebbe essere necessario scegliere tra l'utilizzo di riflessione o non testare affatto.


La linea di fondo è che l'incapsulamento dei dati è un ideale che ci sforziamo di realizzare (in Java), ma ci sono situazioni in cui la cosa pragmaticamente corretta da fare è quello di rompere o ignorare.

Si noti che non tutti i linguaggi OO supportano l'incapsulamento di dati potenti come Java. Ad esempio, Python e Javascript sono entrambi linguaggi OO indiscutibilmente, ma entrambi rendono facile per una classe accedere e modificare lo stato degli oggetti di un'altra classe ... o anche modificare il comportamento delle altre classi. La forte astrazione dei dati non è centrale nella visione di tutti di ciò che significa orientato agli oggetti.

+0

inoltre, puoi violare il concetto OO semplicemente rendendo pubbliche tutte le tue informazioni. :-). Quindi, come puoi vedere, ci sono molti modi per rompere il paradigma OO, non solo per riflettere. La parola chiave privata non è un mezzo per impedire ad altri di modificare le cose, solo per rafforzare il paradigma OO per una migliore comprensione. Se vuoi rovinare tutto ci sono molti modi per farlo. –

+1

@GianlucaGhettini - Direi che se stai rendendo pubblico lo stato, allora non stai "rompendo" l'incapsulamento. In realtà, l'incapsulamento non è lì in primo luogo. (Al contrario, con gli hack riflettenti stai aggirando i limiti di astrazione creati dal designer della classe originale. Stai davvero superando i limiti.) –

+0

Ok, buon punto. Vorrei usare un altro esempio. Il famoso "pubblico privato #define" definisce in C++ :-) –

0

Sì, viola i concetti OO. Tuttavia non infrange alcun modello di sicurezza java. Può essere controllato da java security manager se necessario. La riflessione Java stessa è una cosa utile. Viene utilizzato in annotazioni e IOC che sono concetti molto utili con la possibilità di gestire le classi durante il runtime.

0

Non è possibile combinare Encapsulation, Polymorphism con la riflessione.

La riflessione ha uno scopo completamente diverso. Con il riflesso è possibile creare dinamicamente i tipi ed eseguirli. È possibile accedere ai membri della classe dinamicamente in fase di runtime.


Per esempio, di recente ho avuto utilizzato per l'esecuzione di riflessione Plugins nella mia application.ie ho caricato le dll da una particolare directory, e quindi creato oggetto della classe attraverso la riflessione, gettandola a un'interfaccia condivisa ..

0

L'API di riflessione non modifica lo scopo specifico di Data Incapsulamento?

Non impazzire troppo sulle regole. Ci sono momenti in cui è utile romperli.

La riflessione è molto utile. Prendiamo ad esempio il mio codice per le modifiche di registrazione constatazione in modo che io possa accedere a un database:

public static void log(String userID, Object bOld, Object bNew) 
      throws IntrospectionException, IllegalAccessException, InvocationTargetException { 
     String res = "";//String to hold the change record 
     boolean changed = false; 
     try { 
     if(bOld != null){ //if this is an update 
      BeanInfo beanInfo = Introspector.getBeanInfo(bOld.getClass()); 
      res = bOld.getClass().getSimpleName() + " - "; 
      //loop and compare old values with new values and add them to our string if they are changed 
      for (PropertyDescriptor prop : beanInfo.getPropertyDescriptors()) { 
       Method getter = prop.getReadMethod(); 
       Object vOld = getter.invoke(bOld); //old value 
       Object vNew = getter.invoke(bNew); //new value 
       if (vOld == vNew || (vOld != null && vOld.equals(vNew))) { 
        continue; 
       } 
       changed = true; 
       res = res + "(" + prop.getName() + ", " + vOld + ", " + vNew + ")"; 
      } 
      } 

E 'molto più facile da fare in questo modo. Se stavo usando i getter, dovrei scrivere un metodo separato per ogni classe e modificare ogni volta che è stato aggiunto un nuovo campo. Con la riflessione posso solo scrivere un metodo per gestire tutte le mie classi. Scrivo sul mio metodo per registrare le modifiche here

Problemi correlati