Martin
Martin

Reputation: 11

How to call method from parameter JAVA

my problem is duplicit code. I have 4 methods like below but one is for Name, other for Login, etc. So the only thing that changes are getters (p.getName(), p.getLogin()...).

private Set<Person> filterByName(Set<String> values, Set<Person> peeps){
    Set<Person> filtered = new HashSet<>();
    for (Person p : peeps){
        if(isIn(p.getName(), values)){
            filtered.add(p);
        }
    }
    return filtered;
}

I call these methods in switch:

switch(var){
     case NAME:
          filtered = filterByName(arg1, arg2);
     case ...

How can I do this without implementing a method for each case block?

My idea is to somehow tell the method to use the right getter in case blocks and therefore have only one method for all filtering, e.g.

private Set<Person> universalFilter(Set<String> values, Set<Person> peeps) {
Set<Person> filtered = new HashSet<>();
    for (Person p : peeps){
        if(isIn(***SOMETHING CLEVER***, values)){
            filtered.add(p);
        }
    }
    return filtered;
}

Upvotes: 1

Views: 109

Answers (3)

Arnaud
Arnaud

Reputation: 17524

You could pass another String parameter, which would be the variable on which you want to filter ("Name", "Login"...)

Then with reflection, you could find and call the relevant getter method :

Method method = Person.class.getMethod("get"+ filter, null);

String returnValue = (String)(method.invoke(p, null));

if(isIn(returnValue , values))
...

Upvotes: 0

Hoopje
Hoopje

Reputation: 12932

If you are using Java 8, you can use method references to do exactly what you want. The smallest change is to use a Function object. Your filterByName method takes an additional argument which maps each Person object to the property you want to test:

private Set<Person> filter(Function<Person,String> filter, Set<String> values, Set<Person> peeps) {
    Set<Person> filtered = new HashSet<>();
    for (Person p : peeps){
        if(isIn(filter.apply(p), values)){
            filtered.add(p);
        }
    }
    return filtered;
}

and then you call the method as follows:

filter(Person::getName, values, peeps);

If you want to allow for more complex filters, you can specify the entire filter as a predicate:

private Set<Person> filter(Predicate<Person,String> filter, Set<Person> peeps) {
    Set<Person> filtered = new HashSet<>();
    for (Person p : peeps){
        if (filter.test(p)){
            filtered.add(p);
        }
    }
    return filtered;
}

which can be called with:

filter(p -> isIn(p.getName(), values), peeps); 

In fact, Java 8 provides filtering functionality in the run-time library, either by the removeIf method:

private Set<Person> filter(Predicate<Person,String> filter, Set<Person> peeps) {
    Set<Person> filtered = new HashSet<>();
    filtered.removeIf(filter);
    return filtered;
}

or by using streams:

private Set<Person> filter(Predicate<Person,String> filter, Set<Person> peeps) {
    return peeps.stream().filter(filter).collect(Collectors.toSet());
}

Upvotes: 3

Ryan
Ryan

Reputation: 2084

A simple way is to use the switch variable to pass in as an argument to a new method get() on Person.

if(isIn(p.get(var), values)){

Then implement the new method in Person, eg:

public void get(String var) {
  if (var.equals("NAME")) return name;
  ...
}

Upvotes: 1

Related Questions