Valor de campo obter genérico de reflexão

132

Estou tentando receber o valor do campo via reflexão. O problema é que não conheço o tipo de campos e preciso decidir isso enquanto obtém o valor.

Este código resulta com esta exceção:

Não é possível definir o campo java.lang.String com .... fieldName como java.lang.String

Field field = object.getClass().getDeclaredField(fieldName);
field.setAccessible(true);

Class<?> targetType = field.getType();
Object objectValue = targetType.newInstance();

Object value = field.get(objectValue);

Tentei transmitir, mas recebo erros de compilação:

field.get((targetType)objectValue)

ou

targetType objectValue = targetType.newInstance();

Como posso fazer isso?

Ido Barash
fonte
4
Olhando para a API , o argumento field.get()deve ser object, não objectValue.
IDIOT

Respostas:

144

Como respondido antes, você deve usar:

Object value = field.get(objectInstance);

Outra maneira, que às vezes é preferida, é chamar o getter dinamicamente. código de exemplo:

public static Object runGetter(Field field, BaseValidationObject o)
{
    // MZ: Find the correct method
    for (Method method : o.getMethods())
    {
        if ((method.getName().startsWith("get")) && (method.getName().length() == (field.getName().length() + 3)))
        {
            if (method.getName().toLowerCase().endsWith(field.getName().toLowerCase()))
            {
                // MZ: Method found, run it
                try
                {
                    return method.invoke(o);
                }
                catch (IllegalAccessException e)
                {
                    Logger.fatal("Could not determine method: " + method.getName());
                }
                catch (InvocationTargetException e)
                {
                    Logger.fatal("Could not determine method: " + method.getName());
                }

            }
        }
    }


    return null;
}

Lembre-se também de que quando sua classe é herdada de outra classe, você precisa determinar recursivamente o campo. por exemplo, para buscar todos os campos de uma determinada classe;

    for (Class<?> c = someClass; c != null; c = c.getSuperclass())
    {
        Field[] fields = c.getDeclaredFields();
        for (Field classField : fields)
        {
            result.add(classField);
        }
    }
Marius
fonte
1
não parece exatamente verdade que você precise percorrer as super classes você mesmo. O c.getFields () ou c.getField () pesquisará automaticamente o campo em cada interface do implemento e recursivamente através de todas as superclasses. Portanto, basta mudar para getX de getDeclaredX.
Przemysław Ładyński 25/11
3
De fato, a rotina getFields () permitirá buscar os campos para todas as super classes e interfaces, mas apenas para as públicas. Geralmente, os campos são tornados privados / protegidos e expostos por meio de getters / setters.
Marius
@ Marius, posso saber o que é o pacote BaseValidationObject?
Randytan
@ Randytan, está contido no meu repositório de código privado, você pode substituí-lo por Object. O mesmo se aplica às chamadas estáticas do criador de logs, substitua-as pelo seu próprio criador de logs (instância).
Marius
@Marius a objectclasse não tem o método getMethods(). Algum conselho?
Randytan
127

Você deve passar o objeto para obter o método do campo , para

  Field field = object.getClass().getDeclaredField(fieldName);    
  field.setAccessible(true);
  Object value = field.get(object);
Dmitry Spikhalskiy
fonte
6
você sabe a razão pela qual o objeto deve ser usado em field.get (objeto) - o próprio campo vem desse objeto, por que ele precisa novamente!?
serup
18
@serup Não, o objeto Field vem do objeto Class, que não tem conexão com sua instância real. ( object.getClass()Vontade retornos você Este objeto de classe)
Dmitry Spikhalskiy
1
objectno snippet não está definido para que os leitores não entendam como usá-lo.
Ghilteras 03/04
@Ghilteras, nesse caso, eles não devem usar a reflexão ainda e obter algumas habilidades básicas primeiro 🤷🏻‍♂️. A reflexão é um tópico avançado o suficiente para não explicar que uma variável objectsignifica nosso objeto / instância de destino com o qual trabalhamos. Eu acho que os leitores estão realmente totalmente bem com o que é uma objectresposta nesta resposta.
Dmitry Spikhalskiy 04/04
@RajanPrasad Na verdade não. Há um único objeto na pergunta que possui o nome 'objeto'. Outros objetos têm outros nomes. A resposta é precisa e personalizada para as perguntas e para os nomes usados ​​na pergunta para tornar as coisas o mais claras possível. Se isso não funcionar para você - não tenho idéia de como torná-lo mais claro e você deve tentar outras respostas ou provavelmente evitar a reflexão ainda.
Dmitry Spikhalskiy
19

Uso as reflexões na implementação toString () da minha classe de preferência para ver os membros e os valores da classe (depuração simples e rápida).

O código simplificado que estou usando:

@Override
public String toString() {
    StringBuilder sb = new StringBuilder();

    Class<?> thisClass = null;
    try {
        thisClass = Class.forName(this.getClass().getName());

        Field[] aClassFields = thisClass.getDeclaredFields();
        sb.append(this.getClass().getSimpleName() + " [ ");
        for(Field f : aClassFields){
            String fName = f.getName();
            sb.append("(" + f.getType() + ") " + fName + " = " + f.get(this) + ", ");
        }
        sb.append("]");
    } catch (Exception e) {
        e.printStackTrace();
    }

    return sb.toString();
}

Espero que ajude alguém, porque também procurei.

silversmurf
fonte
12

Embora não esteja realmente claro para mim o que você está tentando alcançar, vi um erro óbvio no seu código: Field.get() espera o objeto que contém o campo como argumento, e não algum valor (possível) desse campo. Então você deveria ter field.get(object).

Como você parece estar procurando o valor do campo, é possível obtê-lo como:

Object objectValue = field.get(object);

Não há necessidade de instanciar o tipo de campo e criar algum valor vazio / padrão; ou talvez haja algo que eu perdi.

Costi Ciudatu
fonte
2
objectnão está definido, os leitores não conseguem entender como aplicar a resposta.
Ghilteras 03/04
10
 Integer typeValue = 0;
 try {
     Class<Types> types = Types.class;
     java.lang.reflect.Field field = types.getDeclaredField("Type");
     field.setAccessible(true);
     Object value = field.get(types);
     typeValue = (Integer) value;
 } catch (Exception e) {
     e.printStackTrace();
 }
Rahul sharma
fonte
4

Você está chamando get com o argumento errado.

Deveria ser:

Object value = field.get(object);
Seba
fonte
2
objectnão está definido, os leitores não conseguem entender como aplicar o exemplo na resposta
Ghilteras 03/04
2

Postei minha solução no Kotlin, mas ela também pode funcionar com objetos java. Eu crio uma extensão de função para que qualquer objeto possa usar essa função.

fun Any.iterateOverComponents() {

val fields = this.javaClass.declaredFields

fields.forEachIndexed { i, field ->

    fields[i].isAccessible = true
    // get value of the fields
    val value = fields[i].get(this)

    // print result
    Log.w("Msg", "Value of Field "
            + fields[i].name
            + " is " + value)
}}

Dê uma olhada nesta página da Web: https://www.geeksforgeeks.org/field-get-method-in-java-with-examples/

Isaias Carrera
fonte
1
    ` 
//Here is the example I used for get the field name also the field value
//Hope This will help to someone
TestModel model = new TestModel ("MyDate", "MyTime", "OUT");
//Get All the fields of the class
 Field[] fields = model.getClass().getDeclaredFields();
//If the field is private make the field to accessible true
fields[0].setAccessible(true);
//Get the field name
  System.out.println(fields[0].getName());
//Get the field value
System.out.println(fields[0].get(model));
`
RANAJEET BARIK
fonte