Meilleur moyen d'invoquer getter par réflexion

127

J'ai besoin d'obtenir la valeur d'un champ avec une annotation spécifique, donc avec la réflexion, je suis capable d'obtenir cet objet de champ. Le problème est que ce champ sera toujours privé bien que je sache à l'avance qu'il aura toujours une méthode getter. Je sais que je peux utiliser setAccesible (true) et obtenir sa valeur (quand il n'y a pas de PermissionManager), bien que je préfère invoquer sa méthode getter.

Je sais que je pourrais rechercher la méthode en recherchant "get + fieldName" (bien que je sache par exemple que les champs booléens sont parfois appelés "is + fieldName").

Je me demande s'il existe une meilleure façon d'invoquer ce getter (de nombreux frameworks utilisent des getters / setters pour accéder aux attributs, alors peut-être qu'ils le font d'une autre manière).

Merci

Javi
la source

Réponses:

241

Je pense que cela devrait vous orienter dans la bonne direction:

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

Notez que vous pouvez créer vous-même des instances BeanInfo ou PropertyDescriptor, c'est-à-dire sans utiliser Introspector. Cependant, Introspector effectue une certaine mise en cache en interne, ce qui est normalement une bonne chose (tm). Si vous êtes heureux sans cache, vous pouvez même opter pour

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

Cependant, il existe de nombreuses bibliothèques qui étendent et simplifient l'API java.beans. Commons BeanUtils est un exemple bien connu. Là, vous feriez simplement:

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

BeanUtils est livré avec d'autres trucs pratiques. c.-à-d. conversion de valeur à la volée (objet en chaîne, chaîne en objet) pour simplifier la définition des propriétés à partir de l'entrée utilisateur.

sfussenegger
la source
Merci beaucoup! Cela m'a épargné les manipulations de cordes, etc.
guerda
1
Bon appel à BeanUtils d'Apache. Facilite l'obtention / la définition des propriétés et gère la conversion de type.
Peter Tseng
Existe-t-il un moyen d'appeler les méthodes dans l'ordre dans lequel les champs sont répertoriés dans le fichier Java?
LifeAndHope
Regardez ma réponse ci-dessous @Anand
Anand
J'ai adoré! Impressionnant.
smilyface
20

Vous pouvez utiliser le framework Reflections pour cela

import static org.reflections.ReflectionUtils.*;
Set<Method> getters = ReflectionUtils.getAllMethods(someClass,
      withModifier(Modifier.PUBLIC), withPrefix("get"), withAnnotation(annotation));
Naveedur Rahman
la source
Attention, Reflections n'est toujours pas compatible avec Java 9 . Il existe des liens pour un meilleur comportement des alternatives ClassIndex (au moment de la compilation) et ClassGraph (au moment de l'exécution) de threre.
Vadzim
Cette solution ne prend pas non plus en compte les getters * contrairement au bean Introspector dans la réponse acceptée.
Vadzim
4

La convention de dénomination fait partie de la spécification JavaBeans bien établie et est prise en charge par les classes du package java.beans .

Michael Borgwardt
la source
3

Vous pouvez invoquer des réflexions et également définir l'ordre de séquence du getter pour les valeurs via des annotations

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

Sortie une fois triée

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