Obter dados da instancia de uma classe com atributos compostos com Reflection

Olá galerinha!!

Queria saber se alguém pode me ajudar a fazer um método que pegue os dados da minha classe: nome do atributo e valor, levando em consideração que o atributo poderá ser composto, ou seja, do tipo outra classe.

Alguém pode me dá uma força :pray:t2::pray:t2::pray:t2:

Oi, não entendi. Você quer um log (salvar em um arquivo) ou só quer printar no console?

Eu quero para montar um query e depois salvar no banco.

Mas inicialmente eu preciso acessar esses valores, porém eu sei pegar atributos simples, mas compostos não.

Pode ser usando ArrayList?

    List<SuaClasse> lista = new ArrayList<>();
    
    SuaClasse mod1 = new SuaClasse();
    mod1.setNome("Abner Rodrigues");
    mod1.setEndereco("Av. Paulista");
    mod1.setCidade("São Paulo");

    SuaClasse mod2 = new SuaClasse("João", "Algum Lugar", "Alguma Cidade");

    lista.add(mod1);
    lista.add(mod2);

    lista.forEach((cliente) -> {
        System.out.println("Cliente: " + cliente.getNome());
    });

SuaClasse, é uma classe Model:

String nome, endereco, cidade;

    public SuaClasse(String nome, String endereco, String cidade) {
        this.nome = nome;
        this.endereco = endereco;
        this.cidade = cidade;
    }

    public SuaClasse() {
    }

    public String getNome() {
        return nome;
    }

    public void setNome(String nome) {
        this.nome = nome;
    }

    public String getEndereco() {
        return endereco;
    }

    public void setEndereco(String endereco) {
        this.endereco = endereco;
    }

    public String getCidade() {
        return cidade;
    }

    public void setCidade(String cidade) {
        this.cidade = cidade;
    }

Saída no console:

Cliente: Abner Rodrigues
Cliente: João
1 curtida

Nesse caso, o @rodriguesabner já preencheu os objetos com os valores. Se você quiser que o usuário vá preenchendo, você pode criar um método criando um objeto, setando as variáveis do mesmo com os valores que o usuário digita e logo após adiciona o objeto para a ArrayList. A ArrayList já terá os campos com as variáveis da SuaClasse, visto que ela é do tipo SuaClasse.

Você consegue fazer isso via reflection, porém, precisa ter alguns cuidados:

  • Certificar-se de que está instanciando a classe correta
  • Validar se o atributo é um tipo primitivo, um objeto de uma classe como String ou algum outro tipo de objeto

A pergunta que não quer calar é: não é possível fazer isso pegando o toString de cada classe?

1 curtida

Olá Darlan!
Você chegou ao ponto que eu queria.
no meu caso o usuário irá solicitar um documento de XML em uma pasta do sistema operacional.
Esse XML irá alimentar a distância de uma determinada classe onde o usuário poderá fazer alterações caso necessite.
Finalmente caso o usuário queira salvar essa instância, ao clicar em salvar o método salvar passa a instância de classe como parâmetro para um método, que deverá recuperar a identificação e o valor de cada atributo para que eu possa formar uma query e depois salvar no banco.
Eu sei fazer isso perfeitamente com uma instância com atributos primitivos mas ao passar para uma instância com atributos compostos eu já não possuo o conhecimento necessário de Reflection para recuperar os valores compostos.
Então gostaria de saber se alguém poderia me ajudar com isso, eu não sei exatamente como fazer a verificação de que tipo de atributo eu estou recebendo, e em relação ao tipo string eu confesso que não entendi muito bem como eu poderia utilizar essa método para o meu propósito, caso possa me explicar ficaria muito grata.

Bom, eu entendo que, de alguma forma, você conhece as classes das quais esses atributos (objetos) vêm.
Logo, minha sugestão seria algo como:

public class Foo {
	private String nome;
	private int idade;
        //getters e setters omitidos
}

public class Boo {
	private int numero;
	private Foo foo;
}

import java.lang.reflect.Method;

public class Teste {

	public static void main(String[] args) throws Exception {
		Boo boo = new Boo();
		Foo foo = new Foo();
		foo.setIdade(20);
		foo.setNome("João");
		boo.setNumero(1);
		boo.setFoo(foo);;
		process(boo);
	}
	
	private static void process(Object obj) throws Exception {
		Class<?> clazz = obj.getClass();
		Method methods[] = clazz.getDeclaredMethods();
		for(Method mtd : methods) {
			if(mtd.getName().startsWith("get")) {
				if(mtd.getReturnType().equals(Foo.class)) {
					System.out.println("This is a foo object");
					Foo boo = (Foo) mtd.invoke(obj);
					System.out.println(boo.getNome());
				}
			}
		}
	}

}

Você pode utilizar recursividade quando encontrar tipos de dado não-primitivos.

Abaixo vou postar uma classe Reflector que você pode utilizar da seguinte forma e ver o resultado gerado:

        Reflector reflector = new Reflector();
        String conteudo = reflector.reflect( instanciaQueVoceQuiser );
        System.out.println(conteudo);

Código da classe Reflector, se entender como o código funciona, deve ficar fácil de adaptar pra sua necessidade:

import java.lang.reflect.Field;

public class Reflector {

    public String reflect(Object object) {
        StringBuilder text = new StringBuilder();
        reflectObject(text, "", object);
        return text.toString();
    }

    private String typeName(Class<?> type) {
        String typeName = type.getName();
        if (!type.isArray()) {
            return typeName;
        }
        int dimensions = typeName.lastIndexOf('[') + 1;
        final String name;
        switch (typeName.charAt(dimensions)) {
            case 'Z':
                name = "boolean";
                break;
            case 'B':
                name = "byte";
                break;
            case 'C':
                name = "char";
                break;
            case 'D':
                name = "double";
                break;
            case 'F':
                name = "float";
                break;
            case 'I':
                name = "int";
                break;
            case 'J':
                name = "long";
                break;
            case 'S':
                name = "short";
                break;
            case 'L':
            default:
                name = typeName.substring(dimensions + 1, typeName.lastIndexOf(';'));
                break;
        }
        StringBuilder text = new StringBuilder(name);
        for (int i = 0; i < dimensions; i++) {
            text.append("[]");
        }
        return text.toString();
    }

    private void reflectObject(StringBuilder text, String indent, Object object) {
        if (object == null) {
            text.append(indent).append("null");
            return;
        }
        Class<?> objectClass = object.getClass();
        String type;
        if (objectClass.isAnnotation()) {
            type = "annotation";
        } else if (objectClass.isEnum()) {
            type = "enum";
        } else if (objectClass.isInterface()) {
            type = "interface";
        } else {
            type = "class";
        }

        text.append(indent).append(type).append(" ").append(typeName(objectClass)).append(" {");
        Field[] attributes = objectClass.getDeclaredFields();
        if (attributes.length > 0) {
            text.append("\n");
            for (Field attribute : attributes) {
                reflectAttribute(text, indent, object, attribute);
            }
        }
        text.append(indent).append("}");
    }

    private void reflectAttribute(StringBuilder text, String indent, Object object, Field attribute) {
        try {
            attribute.setAccessible(true);
            Class<?> attributeType = attribute.getType();
            Object attributeValue = attribute.get(object);
            text.append("\n").append(indent).append("    ").append(attribute.getName()).append(" : ").append(typeName(attributeType));
            if (attributeValue != null && !attributeType.isPrimitive()) {
                text.append(" = (\n\n");
                reflectObject(text, indent + "        ", attributeValue);
                text.append("\n");
                text.append(indent).append("    ").append(");\n");
            } else {
                text.append(";\n");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Se você quiser algo mais bem estruturado e elaborado, poderia cogitar implementar o padrão de projeto Visitor, mas não é algo trivial de se fazer.