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

8 respostas
programação
San_n

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:

8 Respostas

rodriguesabner

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

San_n

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.

rodriguesabner

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
smatt

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.

darlan_machado

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?

San_n

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.

darlan_machado

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

}
staroski

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.

Criado 9 de julho de 2019
Ultima resposta 10 de jul. de 2019
Respostas 8
Participantes 5