pheromix
pheromix

Reputation: 19307

How to make a dynamic method call?

There is a class containing many getters and setters :

@Entity
@Table(name = "valeur_indicateur")
public class ValeurIndicateur {

    @Id
    @SequenceGenerator(name="s_valeur_indicateur", sequenceName="s_valeur_indicateur", allocationSize=1)
    @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="s_valeur_indicateur")
    @Column(name = "val_indi_code")
    private Integer code;

    @ManyToOne
    @JoinColumn(name = "indi_code")
    private Indicateur indicateur;

    @ManyToOne
    @JoinColumn(name = "peri_mes_code")
    private PeriodiciteMesure periodicite_mesure;

    @Column(name = "val_indi_cible_1")
    private String cible_1;

    @Column(name = "val_indi_cible_2")
    private String cible_2;

    @Column(name = "val_indi_cible_3")
    private String cible_3;

    @Column(name = "val_indi_cible_4")
    private String cible_4;

    @Column(name = "val_indi_cible_5")
    private String cible_5;

    @Column(name = "val_indi_cible_6")
    private String cible_6;

    @Column(name = "val_indi_cible_7")
    private String cible_7;

    @Column(name = "val_indi_cible_8")
    private String cible_8;

    @Column(name = "val_indi_cible_9")
    private String cible_9;

    @Column(name = "val_indi_cible_10")
    private String cible_10;

    @Column(name = "val_indi_cible_11")
    private String cible_11;

    @Column(name = "val_indi_cible_12")
    private String cible_12;

    @Column(name = "val_indi_realise_1")
    private String realise_1;

    @Column(name = "val_indi_realise_2")
    private String realise_2;

    @Column(name = "val_indi_realise_3")
    private String realise_3;

    @Column(name = "val_indi_realise_4")
    private String realise_4;

    @Column(name = "val_indi_realise_5")
    private String realise_5;

    @Column(name = "val_indi_realise_6")
    private String realise_6;

    @Column(name = "val_indi_realise_7")
    private String realise_7;

    @Column(name = "val_indi_realise_8")
    private String realise_8;

    @Column(name = "val_indi_realise_9")
    private String realise_9;

    @Column(name = "val_indi_realise_10")
    private String realise_10;

    @Column(name = "val_indi_realise_11")
    private String realise_11;

    @Column(name = "val_indi_realise_12")
    private String realise_12;

    @Column(name = "val_indi_taux")
    private Integer taux;

    public ValeurIndicateur() {
        super();
        // TODO Auto-generated constructor stub
    }

    public ValeurIndicateur(Integer code) {
        super();
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public Indicateur getIndicateur() {
        return indicateur;
    }

    public void setIndicateur(Indicateur indicateur) {
        this.indicateur = indicateur;
    }

    public PeriodiciteMesure getPeriodicite_mesure() {
        return periodicite_mesure;
    }

    public void setPeriodicite_mesure(PeriodiciteMesure periodicite_mesure) {
        this.periodicite_mesure = periodicite_mesure;
    }

    public String getCible_1() {
        return cible_1;
    }

    public void setCible_1(String cible_1) {
        this.cible_1 = cible_1;
    }

    public String getCible_2() {
        return cible_2;
    }

    public void setCible_2(String cible_2) {
        this.cible_2 = cible_2;
    }

    public String getCible_3() {
        return cible_3;
    }

    public void setCible_3(String cible_3) {
        this.cible_3 = cible_3;
    }

    public String getCible_4() {
        return cible_4;
    }

    public void setCible_4(String cible_4) {
        this.cible_4 = cible_4;
    }

    public String getCible_5() {
        return cible_5;
    }

    public void setCible_5(String cible_5) {
        this.cible_5 = cible_5;
    }

    public String getCible_6() {
        return cible_6;
    }

    public void setCible_6(String cible_6) {
        this.cible_6 = cible_6;
    }

    public String getCible_7() {
        return cible_7;
    }

    public void setCible_7(String cible_7) {
        this.cible_7 = cible_7;
    }

    public String getCible_8() {
        return cible_8;
    }

    public void setCible_8(String cible_8) {
        this.cible_8 = cible_8;
    }

    public String getCible_9() {
        return cible_9;
    }

    public void setCible_9(String cible_9) {
        this.cible_9 = cible_9;
    }

    public String getCible_10() {
        return cible_10;
    }

    public void setCible_10(String cible_10) {
        this.cible_10 = cible_10;
    }

    public String getCible_11() {
        return cible_11;
    }

    public void setCible_11(String cible_11) {
        this.cible_11 = cible_11;
    }

    public String getCible_12() {
        return cible_12;
    }

    public void setCible_12(String cible_12) {
        this.cible_12 = cible_12;
    }

    public String getRealise_1() {
        return realise_1;
    }

    public void setRealise_1(String realise_1) {
        this.realise_1 = realise_1;
    }

    public String getRealise_2() {
        return realise_2;
    }

    public void setRealise_2(String realise_2) {
        this.realise_2 = realise_2;
    }

    public String getRealise_3() {
        return realise_3;
    }

    public void setRealise_3(String realise_3) {
        this.realise_3 = realise_3;
    }

    public String getRealise_4() {
        return realise_4;
    }

    public void setRealise_4(String realise_4) {
        this.realise_4 = realise_4;
    }

    public String getRealise_5() {
        return realise_5;
    }

    public void setRealise_5(String realise_5) {
        this.realise_5 = realise_5;
    }

    public String getRealise_6() {
        return realise_6;
    }

    public void setRealise_6(String realise_6) {
        this.realise_6 = realise_6;
    }

    public String getRealise_7() {
        return realise_7;
    }

    public void setRealise_7(String realise_7) {
        this.realise_7 = realise_7;
    }

    public String getRealise_8() {
        return realise_8;
    }

    public void setRealise_8(String realise_8) {
        this.realise_8 = realise_8;
    }

    public String getRealise_9() {
        return realise_9;
    }

    public void setRealise_9(String realise_9) {
        this.realise_9 = realise_9;
    }

    public String getRealise_10() {
        return realise_10;
    }

    public void setRealise_10(String realise_10) {
        this.realise_10 = realise_10;
    }

    public String getRealise_11() {
        return realise_11;
    }

    public void setRealise_11(String realise_11) {
        this.realise_11 = realise_11;
    }

    public String getRealise_12() {
        return realise_12;
    }

    public void setRealise_12(String realise_12) {
        this.realise_12 = realise_12;
    }

    public Integer getTaux() {
        return taux;
    }

    public void setTaux(Integer taux) {
        this.taux = taux;
    }

    @Override
    public String toString() {
        // TODO Auto-generated method stub
        return code.toString();
    }

}

I want to call the setters setCible_ from 1 to 12 :

@RequestMapping(value = "/insertIndicateur", method = RequestMethod.POST)
public ModelAndView insertIndicateur(@ModelAttribute("indicateur_formulaire") Indicateur indicateur,
                                    @RequestParam int f_objectif,
                                    @RequestParam int f_nature_indicateur,
                                    @RequestParam String f_periodicite_mesure,
                                    @RequestParam String f_type_sortie,
                                    @RequestParam String acteur_saisie,
                                    @RequestParam String acteur_verif,
                                    @RequestParam Map<String, String> params,
                                    HttpServletRequest request, HttpSession session) {

    indicateurDao.insert(indicateur, f_objectif, f_nature_indicateur, f_periodicite_mesure, f_type_sortie, acteur_saisie, acteur_verif);

    ValeurIndicateur valeurIndicateur = new ValeurIndicateur();

    switch(f_periodicite_mesure) {
        case "ANN":
            valeurIndicateur.setCible_1(params.get("val_indi_cible_1"));
            break;
        case "SEM":
            valeurIndicateur.setCible_1(params.get("val_indi_cible_1"));
            valeurIndicateur.setCible_2(params.get("val_indi_cible_2"));
            break;
        case "TRI":
            valeurIndicateur.setCible_1(params.get("val_indi_cible_1"));
            valeurIndicateur.setCible_2(params.get("val_indi_cible_2"));
            valeurIndicateur.setCible_3(params.get("val_indi_cible_3"));
            valeurIndicateur.setCible_4(params.get("val_indi_cible_4"));
            break;
        case "MEN":

            // here I want to loop from 1 to 12 and call dynamically valeurIndicateur.setCible_

            break;
    }

    valeurIndicateur.setIndicateur(indicateur);
    valeurIndicateur.setPeriodicite_mesure(periodiciteMesureDao.get(f_periodicite_mesure));

    valeurIndicateurDao.insert(valeurIndicateur);

    ModelAndView modelView = new ModelAndView("redirect:/elaboration/");

    return modelView;

}

Is there a way to dynamically call them ?

Upvotes: 0

Views: 298

Answers (2)

Sean Patrick Floyd
Sean Patrick Floyd

Reputation: 298898

If your Object conforms with the JavaBeans specification, then (as you are using Spring), the BeanWrapper interface is probably your best bet:

BeanWrapper beanWrapper = new BeanWrapperImpl(yourObject);
Arrays.stream(beanWrapper.getPropertyDescriptors())
      .map(PropertyDescriptor::getName)
      .forEach(name-> beanWrapper.setPropertyValue(name, attributes.get(name))
);

Note that the JavaBeans property name "foo" points at the methods "setFoo" and "getFoo".

Or, if you want to iterate over the attributes map, do it like this:

BeanWrapper beanWrapper = new BeanWrapperImpl(yourObject);
attributes.forEach((k,v)->{
    if(beanWrapper.isWritableProperty(k)) beanWrapper.setPropertyValue(k,v);
});

Another way without reflection is to have a hard coded map with BiConsumers that call the methods, something like this:

static final Map<String, BiConsumer<YourClass,String>> PROPERTY_MAP;
static {
    Map<String, BiConsumer<YourClass,String>> map = new HashMap<>();
    map.put("val_indi_cible_1", YourClass::setCible_1);
    map.put("val_indi_cible_2", YourClass::setCible_2);
    map.put("val_indi_cible_3", YourClass::setCible_3);
    map.put("val_indi_cible_4", YourClass::setCible_3);
    map.put("val_indi_cible_5", YourClass::setCible_4);
    map.put("val_indi_cible_6", YourClass::setCible_5);
    // etc.
    PROPERTY_MAP= Collections.unmodifiableMap(map);
}

Now, in your method, you can do something like this:

YourClass yourClass = new YourClass();
attributes.forEach((k, v) -> Optional.ofNullable(PROPERTY_MAP.get(k))
                                     .ifPresent(bc -> bc.accept(yourClass, v)));

Explanation:

  • Iterate over attributes map
  • For each key, see if there is an associated property
  • If present, call that property

Advantage: no reflection, decent performance, compile-time safety

Disadvantage: you have to update the property map whenever you add a new method

Upvotes: 2

GhostCat
GhostCat

Reputation: 140457

Dynamically boils down to use the reflection utilities.

So, yes; you can use reflection to invoke methods by name on your valeurIndicateur object. And of course, you can build those method names using a base string that gets some index appended to it.

But then, doing so comes with the usual "reflection bill"; as reflection impacts performance, and code readability/robustness in a (sometimes very) negative way.

In your case: I am wondering if you shouldn't rather look into reworking your whole model. Why are you explicitly using names like c1, c2, c3, ... in the first place? Wouldn't make much more sense to use some form of list/array instead?! Meaning: to me it seems as this question is a result of a bad design decision; and instead of fixing that real problem; you are asking "how do I mitigate the symptoms"?

Upvotes: 1

Related Questions