La mejor manera de invocar a getter por reflexión

127

Necesito obtener el valor de un campo con una anotación específica, así que con la reflexión puedo obtener este objeto de campo. El problema es que este campo siempre será privado, aunque sé de antemano que siempre tendrá un método getter. Sé que puedo usar setAccesible (true) y obtener su valor (cuando no hay PermissionManager), aunque prefiero invocar su método getter.

Sé que podría buscar el método buscando "get + fieldName" (aunque sé, por ejemplo, que los campos booleanos a veces se denominan "is + fieldName").

Me pregunto si hay una mejor manera de invocar este captador (muchos marcos usan captadores / establecedores para acceder a los atributos, por lo que tal vez lo hagan de otra manera).

Gracias

Javi
fuente

Respuestas:

241

Creo que esto debería apuntar hacia la dirección correcta:

import java.beans.*

for (PropertyDescriptor pd : Introspector.getBeanInfo(Foo.class).getPropertyDescriptors()) {
  if (pd.getReadMethod() != null && !"class".equals(pd.getName()))
    System.out.println(pd.getReadMethod().invoke(foo));
}

Tenga en cuenta que puede crear instancias BeanInfo o PropertyDescriptor usted mismo, es decir, sin usar Introspector. Sin embargo, Introspector realiza un almacenamiento en caché internamente, que normalmente es una buena cosa (tm). Si eres feliz sin un caché, incluso puedes ir por

// TODO check for non-existing readMethod
Object value = new PropertyDescriptor("name", Person.class).getReadMethod().invoke(person);

Sin embargo, hay muchas bibliotecas que amplían y simplifican la API java.beans. Commons BeanUtils es un ejemplo bien conocido. Allí, simplemente harías:

Object value = PropertyUtils.getProperty(person, "name");

BeanUtils viene con otras cosas útiles. es decir, conversión de valor sobre la marcha (objeto a cadena, cadena a objeto) para simplificar la configuración de propiedades desde la entrada del usuario.

sfussenegger
fuente
¡Muchas gracias! Esto me salvó de manipulaciones de cuerdas, etc.
guerda
1
Buena llamada en BeanUtils de Apache. Facilita la obtención / configuración de propiedades y maneja la conversión de tipos.
Peter Tseng
¿Hay alguna manera de invocar los métodos en el orden en que se enumeran los campos en el archivo Java?
LifeAndHope
Mira mi respuesta a continuación @Anand
Anand
Me encantó ! Increíble.
smilyface
20

Puede usar el marco Reflections para esto

import static org.reflections.ReflectionUtils.*;
Set<Method> getters = ReflectionUtils.getAllMethods(someClass,
      withModifier(Modifier.PUBLIC), withPrefix("get"), withAnnotation(annotation));
Naveedur Rahman
fuente
Tenga en cuenta que Reflections todavía no es compatible con Java 9 . Hay enlaces para alternativas de comportamiento mejor ClassIndex (tiempo de compilación) y ClassGraph (tiempo de ejecución) de tres.
Vadzim
Esta solución tampoco tiene en cuenta los * getters a diferencia del Beans Introspector en la respuesta aceptada.
Vadzim
4

La convención de nomenclatura es parte de la especificación JavaBeans bien establecida y es compatible con las clases en el paquete java.beans .

Michael Borgwardt
fuente
3

Puede invocar reflexiones y también establecer el orden de secuencia para getter para valores a través de anotaciones

public class Student {

    private String grade;

    private String name;

    private String id;

    private String gender;

    private Method[] methods;

    @Retention(RetentionPolicy.RUNTIME)
    public @interface Order {
        int value();
    }

    /**
     * Sort methods as per Order Annotations
     * 
     * @return
     */
    private void sortMethods() {

        methods = Student.class.getMethods();

        Arrays.sort(methods, new Comparator<Method>() {
            public int compare(Method o1, Method o2) {
                Order or1 = o1.getAnnotation(Order.class);
                Order or2 = o2.getAnnotation(Order.class);
                if (or1 != null && or2 != null) {
                    return or1.value() - or2.value();
                }
                else if (or1 != null && or2 == null) {
                    return -1;
                }
                else if (or1 == null && or2 != null) {
                    return 1;
                }
                return o1.getName().compareTo(o2.getName());
            }
        });
    }

    /**
     * Read Elements
     * 
     * @return
     */
    public void readElements() {
        int pos = 0;
        /**
         * Sort Methods
         */
        if (methods == null) {
            sortMethods();
        }
        for (Method method : methods) {
            String name = method.getName();
            if (name.startsWith("get") && !name.equalsIgnoreCase("getClass")) {
                pos++;
                String value = "";
                try {
                    value = (String) method.invoke(this);
                }
                catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
                    e.printStackTrace();
                }
                System.out.println(name + " Pos: " + pos + " Value: " + value);
            }
        }
    }

    // /////////////////////// Getter and Setter Methods

    /**
     * @param grade
     * @param name
     * @param id
     * @param gender
     */
    public Student(String grade, String name, String id, String gender) {
        super();
        this.grade = grade;
        this.name = name;
        this.id = id;
        this.gender = gender;
    }

    /**
     * @return the grade
     */
    @Order(value = 4)
    public String getGrade() {
        return grade;
    }

    /**
     * @param grade the grade to set
     */
    public void setGrade(String grade) {
        this.grade = grade;
    }

    /**
     * @return the name
     */
    @Order(value = 2)
    public String getName() {
        return name;
    }

    /**
     * @param name the name to set
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * @return the id
     */
    @Order(value = 1)
    public String getId() {
        return id;
    }

    /**
     * @param id the id to set
     */
    public void setId(String id) {
        this.id = id;
    }

    /**
     * @return the gender
     */
    @Order(value = 3)
    public String getGender() {
        return gender;
    }

    /**
     * @param gender the gender to set
     */
    public void setGender(String gender) {
        this.gender = gender;
    }

    /**
     * Main
     * 
     * @param args
     * @throws IOException
     * @throws SQLException
     * @throws InvocationTargetException
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     */
    public static void main(String args[]) throws IOException, SQLException, IllegalAccessException,
            IllegalArgumentException, InvocationTargetException {
        Student student = new Student("A", "Anand", "001", "Male");
        student.readElements();
    }
  }

Salida cuando se ordena

getId Pos: 1 Value: 001
getName Pos: 2 Value: Anand
getGender Pos: 3 Value: Male
getGrade Pos: 4 Value: A
Anand
fuente