Reflexión genérica obtener valor de campo

132

Estoy tratando de recibir el valor del campo a través de la reflexión. El problema es que no sé el tipo de campo y tengo que decidirlo mientras obtengo el valor.

Este código resulta con esta excepción:

No se puede establecer java.lang.String field com .... fieldName en 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);

Traté de emitir, pero recibo errores de compilación:

field.get((targetType)objectValue)

o

targetType objectValue = targetType.newInstance();

¿Cómo puedo hacer esto?

Ido Barash
fuente
44
Mirando la API , el argumento field.get()debería ser object, no objectValue.
akaIDIOT

Respuestas:

144

Como respondió antes, debe usar:

Object value = field.get(objectInstance);

Otra forma, que a veces se prefiere, es llamar al captador de forma dinámica. código de ejemplo:

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

También tenga en cuenta que cuando su clase hereda de otra clase, debe determinar recursivamente el campo. por ejemplo, para buscar todos los campos de una clase determinada;

    for (Class<?> c = someClass; c != null; c = c.getSuperclass())
    {
        Field[] fields = c.getDeclaredFields();
        for (Field classField : fields)
        {
            result.add(classField);
        }
    }
Mario
fuente
1
no parece exactamente cierto que deba iterar a través de superclases usted mismo. C.getFields () o c.getField () buscarán automáticamente el campo en cada interfaz de implemento y de forma recursiva en todas las superclases. Entonces, es suficiente cambiar a getX desde getDeclaredX.
Przemysław Ładyński
3
De hecho, la rutina getFields () le permitirá buscar los campos para todas las superclases e interfaces, pero solo las públicas. Por lo general, los campos se hacen privados / protegidos y se exponen a través de getters / setters.
Marius
@ Mario, ¿puedo saber qué es el paquete BaseValidationObject?
randytan
@Randytan, está contenido en mi repositorio de código privado, puede reemplazarlo con Object. Lo mismo se aplica a las llamadas estáticas del registrador, reemplácelas con su propio registrador (instancia).
Marius
@ Mario la objectclase no tiene el método getMethods(). ¿Algún consejo?
randytan
127

Debe pasar el objeto para obtener el método del campo , por lo que

  Field field = object.getClass().getDeclaredField(fieldName);    
  field.setAccessible(true);
  Object value = field.get(object);
Dmitry Spikhalskiy
fuente
66
¿Conoces la razón por la cual el objeto tiene que usarse en field.get (objeto): el campo en sí proviene de ese objeto, ¿por qué lo necesita de nuevo?
serup
18
@serup No, el objeto Field proviene del objeto Class, que no tiene conexión con su instancia real. ( object.getClass()le devolverá este objeto de clase)
Dmitry Spikhalskiy
1
objecten el fragmento no está definido, por lo que los lectores no pueden entender cómo usarlo.
Ghilteras
@Ghilteras en ese caso, no deberían usar la reflexión todavía y obtener algunas habilidades básicas primero 🤷🏻‍♂️. La reflexión es un tema lo suficientemente avanzado como para no explicar que una variable objectsignifica nuestro objeto / instancia de destino con el que trabajamos. Creo que los lectores están totalmente de acuerdo con lo que hay objecten esta respuesta.
Dmitry Spikhalskiy
@RajanPrasad No realmente. Hay un solo objeto en la pregunta que tiene un nombre 'objeto'. Otros objetos tienen otros nombres. La respuesta es precisa y adaptada a las preguntas y a los nombres que se utilizan en la pregunta para aclarar todo lo posible. Si no funciona para usted, no tengo idea de cómo hacerlo más claro y debería intentar otras respuestas o probablemente evitar la reflexión todavía.
Dmitry Spikhalskiy
19

Utilizo las reflexiones en la implementación toString () de mi clase de preferencia para ver los miembros y valores de la clase (depuración simple y rápida).

El código simplificado que estoy 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 ayude a alguien, porque también he buscado.

silversmurf
fuente
12

Aunque en realidad no está claro para mí lo que está tratando de lograr, detecté un error obvio en su código: Field.get()espera el objeto que contiene el campo como argumento, no algún valor (posible) de ese campo. Entonces deberías haberlo hecho field.get(object).

Como parece estar buscando el valor del campo, puede obtenerlo como:

Object objectValue = field.get(object);

No es necesario instanciar el tipo de campo y crear algún valor vacío / predeterminado; o tal vez hay algo que me perdí.

Costi Ciudatu
fuente
2
objectno está definido, los lectores no pueden entender cómo aplicar la respuesta.
Ghilteras
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
fuente
4

Estás llamando a get con el argumento equivocado.

Debería ser:

Object value = field.get(object);
Seba
fuente
2
objectno está definido, los lectores no pueden entender cómo aplicar el ejemplo en la respuesta
Ghilteras
2

Publico mi solución en Kotlin, pero también puede funcionar con objetos Java. Creo una extensión de función para que cualquier objeto pueda usar esta función.

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

Eche un vistazo a esta página web: https://www.geeksforgeeks.org/field-get-method-in-java-with-examples/

Isaias Carrera
fuente
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
fuente