2

At work, we have to generate a report for our client that changes its parameters several times during the week. This report is generated from a single table on our database. For example, imagine a table that has 100 columns and I have to generate a report with only 5 columns today, but tomorrow I have to generate with 95 of them. With this in mind, I created a TO class with all the columns of the specified table and my query returns all columns (SELECT * FROM TABLE).

What I'm trying to create is a dynamic form to generate the report. I first thought on create a simple frame with a list of the columns listed as check boxes and the user would select the columns that he wants (of course with a button to Select All and another to Deselect All).

As all of the columns have the same name as the attributes of the TO class, I developed the following code (I have Google this):

Class c = Test.class;

for(int i = 0; i < listOfAttributes.length; i++)
{
    auxText += String.valueOf( c.getMethod( "get" + listOfAttributes[i]).invoke( this, null ) );
}

Is this the better way to do what I need to?

Thanks in advance.

Obs.: the getters of the TO class have the pattern "getAttribute_Name".

Note: This question is different from the one where the user is asking HOW to invoke some method given a certain name. I know how to do that. What I'm asking is if this is the better way to solve the problem I described.

Andrew Regan
  • 5,087
  • 6
  • 37
  • 73
Victor Dinis
  • 111
  • 1
  • 5
  • 1
    Possible duplicate of [How do I invoke a Java method when given the method name as a string?](http://stackoverflow.com/questions/160970/how-do-i-invoke-a-java-method-when-given-the-method-name-as-a-string) – Claudia Apr 30 '16 at 16:40
  • Like I said on the note: This question is different from the one where the user is asking HOW to invoke some method given a certain name. I know how to do that. What I'm asking is if this is the better way to solve the problem I described. – Victor Dinis May 01 '16 at 16:11
  • Okay. I'll take that back. I will note that your note was added after I first reviewed it. – Claudia May 02 '16 at 22:24
  • *"What I'm asking is if this is the better way to solve the problem I described."* ... best practice questions are very often a matter of opinion. I believe it will be in this case as well. – scottb May 03 '16 at 00:22

2 Answers2

0

My Java is a little more limited, but I believe that's about as good as you're going to get using reflection.

Class<?> c = Test.class;

for (String attribute : listOfAttributes) {
    auxText += String.valueOf(c.getMethod("get" + attribute).invoke(this, null));
}

But since this sounds like it's from potentially untrusted data, I would advise using a HashMap in this case, with each method explicitly referenced. First of all, it explicitly states what methods can be dynamically called. Second, it's more type safe, and compile-time errors are way better than runtime errors. Third, it is likely faster, since it avoids reflection altogether. Something to the effect of this:

private static final HashMap<String, Supplier<Object>> methods = new HashMap<>();

// Initialize all the methods.
static {
    methods.set("Foo", Test::getFoo);
    methods.set("Bar", Test::getBar);
    // etc.
}

private String invokeGetter(String name) {
    if (methods.containsKey(name)) {
        return String.valueOf(methods.get(name).get());
    } else {
        throw new NoSuchMethodException();
    }
}

It might sound like a major DRY violation to do so, but the repetition at least makes sure you don't wind up with unrelated getters accidentally called.

Claudia
  • 1,197
  • 15
  • 30
0
Class c = Test.class;
for(int i = 0; i < listOfAttributes.length; i++)
{
    auxText += String.valueOf( c.getMethod( "get" + listOfAttributes[i]).invoke( this, null ) );
}

You can do this somewhat more elegantly via Java Beans, the Introspector, and PropertyDescriptor, but it's a little more long-winded:

Map<String, Method> methods = new HashMap<>();
Class c = this.getClass(); // surely?
for (PropertyDescriptor pd : Introspector.getBeanInfo(c).getPropertyDescriptors())
{
    map.put(pd.getName(), pd.getReadMethod();
}
// 
for (int i = 0; i < listOfAttributes.length; i++)
{
    Method m = methods.get(listOfAttributes[i]);
    if (m == null)
        continue;
    auxText += String.valueOf(m.invoke(this, null));
}
user207421
  • 305,947
  • 44
  • 307
  • 483