Como fazer um loop em atributos de classe em Java?

87

Como posso fazer um loop sobre os atributos de uma classe em java dinamicamente.

Por exemplo:

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

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

isso é possível em Java?

Zakaria
fonte

Respostas:

101

Não há suporte linguístico para fazer o que você está pedindo.

Você pode acessar reflexivamente os membros de um tipo em tempo de execução usando reflexão (por exemplo, com Class.getDeclaredFields()para obter um array de Field), mas dependendo do que você está tentando fazer, esta pode não ser a melhor solução.

Veja também

Perguntas relacionadas


Exemplo

Aqui está um exemplo simples para mostrar apenas parte do que a reflexão é capaz de fazer.

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()
            );
        }
    }
}

O trecho acima usa reflexão para inspecionar todos os campos declarados de class String; ele produz a seguinte saída:

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: Prefere interfaces para reflexão

Estes são trechos do livro:

Dado um Classobjeto, você pode obter Constructor, Methode Fieldinstâncias que representam os construtores, métodos e campos da classe. [Eles] permitem que você manipule reflexivamente suas contrapartes subjacentes . Esse poder, no entanto, tem um preço:

  • Você perde todos os benefícios da verificação em tempo de compilação.
  • O código necessário para realizar o acesso reflexivo é desajeitado e prolixo.
  • O desempenho sofre.

Como regra, os objetos não devem ser acessados ​​reflexivamente em aplicativos normais em tempo de execução.

Existem alguns aplicativos sofisticados que requerem reflexão. Os exemplos incluem [... omitido propositalmente ...] Se você tiver alguma dúvida se seu aplicativo se enquadra em uma dessas categorias, provavelmente não.

poligenelubrificantes
fonte
na verdade, de acordo com o valor dos campos, quero escrever ou não escrever o valor de cada campo?
Zakaria
@Zakaria: sim, você pode fazer isso refletindo, mas dependendo do que você está tentando fazer, existem designs muito melhores.
poligenelubrificantes
Na verdade, tenho um relatório para gerar a partir de uma classe e, dependendo do valor dos campos da classe que desejo colocar ou não nesses campos, qual é o melhor design que posso implementar?
Zakaria
1
@Zakaria: então vá em frente e experimente a reflexão. Lá Field.getvocê pode usar para ler os valores de um campo reflexivamente. Se for private, talvez você consiga contornar isso com setAccessible. Sim, a reflexão é muito poderosa e permite que você faça coisas como inspecionar privatecampos, sobrescrever finalcampos, invocar privateconstrutores, etc. Se precisar de mais ajuda com o aspecto do design, você provavelmente deve escrever uma nova pergunta com muito mais informações. Talvez você não precise desses muitos atributos em primeiro lugar (por exemplo, use enumou List, etc).
poligenelubrificantes
1
genéricos não tem uso aqui. basta fazerstatic void inspect(Class<?> klazz)
user102008
45

Acessar os campos diretamente não é um estilo muito bom em java. Eu sugeriria criar métodos getter e setter para os campos de seu bean e então usar as classes Introspector e BeanInfo do pacote 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);
}
Jörn Horstmann
fonte
Existe alguma maneira de obter apenas propriedades do bean, não da classe? ou seja, evite MyBean.class retornado por getPropertyDescriptors
hbprotoss
@hbprotoss se bem entendi, você pode verificar propertyDesc.getReadMethod().getDeclaringClass() != Object.classou especificar uma classe para interromper a análise como o segundo parâmetro getBeanInfo(MyBean.class, Object.class).
Jörn Horstmann
20

Embora eu concorde com a resposta de Jörn se sua classe está em conformidade com a especificação JavaBeabs, aqui está uma boa alternativa se não estiver e você usar Spring.

Spring tem uma classe chamada ReflectionUtils que oferece algumas funcionalidades muito poderosas, incluindo doWithFields (classe, retorno de chamada) , um método de estilo de visitante que permite iterar sobre os campos de uma classe usando um objeto de retorno de chamada como este:

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));

    });
}

Mas aqui está um aviso: a classe é rotulada como "apenas para uso interno", o que é uma pena se você me perguntar

Sean Patrick Floyd
fonte
14

Maneira simples de iterar nos campos da classe e obter valores do objeto:

 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) {
      }
 }
Rickey
fonte
Uma classe talvez ou qualquer objeto.
Rickey
@Rickey Existe alguma maneira de definir um novo valor para os atributos fazendo um loop em cada campo?
hyperfkcb
@hyperfkcb Você pode obter uma exceção de modificação ao tentar alterar os valores das propriedades / campos sobre os quais está iterando.
Rickey
6

Java tem Reflection (java.reflection. *), Mas eu sugiro olhar em uma biblioteca como o Apache Beanutils, isso tornará o processo muito menos complicado do que usar reflexão diretamente.

Tassos Bassoukos
fonte
0

Aqui está uma solução que classifica as propriedades em ordem alfabética e as imprime todas junto com seus valores:

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());
}
Benny Neugebauer
fonte