Come eseguire il loop degli attributi di una classe in Java?


87

Come posso eseguire il loop degli attributi di una classe in java in modo dinamico.

Ad esempio:

public class MyClass{
    private type1 att1;
    private type2 att2;
    ...

    public void function(){
        for(var in MyClass.Attributes){
            System.out.println(var.class);
        }
    }
}

è possibile in Java?

Risposte:


101

Non c'è supporto linguistico per fare ciò che chiedi.

È possibile accedere in modo riflessivo ai membri di un tipo in fase di esecuzione utilizzando la riflessione (ad esempio con Class.getDeclaredFields()per ottenere un array di Field), ma a seconda di ciò che si sta tentando di fare, questa potrebbe non essere la soluzione migliore.

Guarda anche

Domande correlate


Esempio

Ecco un semplice esempio per mostrare solo alcune delle cose che la riflessione è in grado di fare.

import java.lang.reflect.*;

public class DumpFields {
    public static void main(String[] args) {
        inspect(String.class);
    }
    static <T> void inspect(Class<T> klazz) {
        Field[] fields = klazz.getDeclaredFields();
        System.out.printf("%d fields:%n", fields.length);
        for (Field field : fields) {
            System.out.printf("%s %s %s%n",
                Modifier.toString(field.getModifiers()),
                field.getType().getSimpleName(),
                field.getName()
            );
        }
    }
}

Lo snippet di cui sopra utilizza la riflessione per ispezionare tutti i campi dichiarati di class String; produce il seguente output:

7 fields:
private final char[] value
private final int offset
private final int count
private int hash
private static final long serialVersionUID
private static final ObjectStreamField[] serialPersistentFields
public static final Comparator CASE_INSENSITIVE_ORDER

Effective Java 2nd Edition, Item 53: Preferisci le interfacce alla reflection

Questi sono estratti dal libro:

Dato un Classoggetto, è possibile ottenere Constructor, Methode Fieldistanze che rappresentano i costruttori, i metodi e i campi della classe. [Loro] ti permettono di manipolare le loro controparti sottostanti in modo riflessivo . Questo potere, tuttavia, ha un prezzo:

  • Perdi tutti i vantaggi del controllo in fase di compilazione.
  • Il codice richiesto per eseguire l'accesso riflessivo è goffo e dettagliato.
  • Le prestazioni ne risentono.

Di norma, non si dovrebbe accedere agli oggetti in modo riflessivo nelle normali applicazioni in fase di runtime.

Esistono alcune applicazioni sofisticate che richiedono una riflessione. Gli esempi includono [... omesso di proposito ...] Se hai dei dubbi sul fatto che la tua applicazione rientri in una di queste categorie, probabilmente non è così.


infatti in base al valore dei campi, voglio scrivere o meno il valore di ogni campo?
Zakaria

@Zakaria: sì, puoi farlo con la riflessione, ma a seconda di cosa stai cercando di fare, ci sono progetti molto migliori.
lubrificanti poligenici

In effetti, ho un report da generare da una classe e, a seconda del valore dei campi della classe che voglio mettere o meno in quei campi, qual è il miglior design che posso implementare.?
Zakaria

1
@Zakaria: vai avanti e prova a riflettere. C'è Field.getche puoi usare per leggere i valori di un campo in modo riflessivo. Se lo è private, potresti essere in grado di aggirare questo problema setAccessible. Sì, la riflessione è molto potente e ti consente di fare cose come ispezionare privatecampi, sovrascrivere finalcampi, invocare privatecostruttori, ecc. Se hai bisogno di ulteriore assistenza con l'aspetto del design, dovresti probabilmente scrivere una nuova domanda con molte più informazioni. Forse non hai bisogno di questi molti attributi in primo luogo (ad esempio, usa enumo List, ecc.).
polygenelubricants

1
i generici non servono qui. fallostatic void inspect(Class<?> klazz)
user102008

45

L'accesso diretto ai campi non è uno stile molto buono in java. Suggerirei di creare metodi getter e setter per i campi del tuo bean e quindi utilizzare le classi Introspector e BeanInfo dal pacchetto java.beans.

MyBean bean = new MyBean();
BeanInfo beanInfo = Introspector.getBeanInfo(MyBean.class);
for (PropertyDescriptor propertyDesc : beanInfo.getPropertyDescriptors()) {
    String propertyName = propertyDesc.getName();
    Object value = propertyDesc.getReadMethod().invoke(bean);
}

C'è un modo per ottenere solo le proprietà del bean, non della classe? ovvero evitare MyBean.class restituito da getPropertyDescriptors
hbprotoss

@hbprotoss se ti ho capito correttamente, puoi controllare propertyDesc.getReadMethod().getDeclaringClass() != Object.class, oppure puoi specificare una classe per interrompere l'analisi come secondo parametro come getBeanInfo(MyBean.class, Object.class).
Jörn Horstmann

20

Sebbene sia d'accordo con la risposta di Jörn se la tua classe è conforme alle specifiche JavaBeabs, ecco una buona alternativa se non lo fa e usi Spring.

Spring ha una classe denominata ReflectionUtils che offre alcune funzionalità molto potenti, tra cui doWithFields (class, callback) , un metodo in stile visitatore che ti consente di iterare sui campi di una classe utilizzando un oggetto callback come questo:

public void analyze(Object obj){
    ReflectionUtils.doWithFields(obj.getClass(), field -> {

        System.out.println("Field name: " + field.getName());
        field.setAccessible(true);
        System.out.println("Field value: "+ field.get(obj));

    });
}

Ma ecco un avvertimento: la classe è etichettata come "solo per uso interno", il che è un peccato se me lo chiedi


14

Modo semplice per iterare sui campi della classe e ottenere valori dall'oggetto:

 Class<?> c = obj.getClass();
 Field[] fields = c.getDeclaredFields();
 Map<String, Object> temp = new HashMap<String, Object>();

 for( Field field : fields ){
      try {
           temp.put(field.getName().toString(), field.get(obj));
      } catch (IllegalArgumentException e1) {
      } catch (IllegalAccessException e1) {
      }
 }

Una classe forse o qualsiasi oggetto.
Rickey

@Rickey Esiste comunque un nuovo valore per gli attributi ripetendo ogni campo?
hyperfkcb

@hyperfkcb Potresti ricevere un'eccezione di modifica quando tenti di cambiare i valori delle proprietà / campi su cui stai iterando.
Rickey

6

Java ha Reflection (java.reflection. *), Ma suggerirei di esaminare una libreria come Apache Beanutils, renderà il processo molto meno complicato rispetto all'utilizzo diretto della riflessione.


0

Ecco una soluzione che ordina alfabeticamente le proprietà e le stampa tutte insieme ai loro valori:

public void logProperties() throws IllegalArgumentException, IllegalAccessException {
  Class<?> aClass = this.getClass();
  Field[] declaredFields = aClass.getDeclaredFields();
  Map<String, String> logEntries = new HashMap<>();

  for (Field field : declaredFields) {
    field.setAccessible(true);

    Object[] arguments = new Object[]{
      field.getName(),
      field.getType().getSimpleName(),
      String.valueOf(field.get(this))
    };

    String template = "- Property: {0} (Type: {1}, Value: {2})";
    String logMessage = System.getProperty("line.separator")
            + MessageFormat.format(template, arguments);

    logEntries.put(field.getName(), logMessage);
  }

  SortedSet<String> sortedLog = new TreeSet<>(logEntries.keySet());

  StringBuilder sb = new StringBuilder("Class properties:");

  Iterator<String> it = sortedLog.iterator();
  while (it.hasNext()) {
    String key = it.next();
    sb.append(logEntries.get(key));
  }

  System.out.println(sb.toString());
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.