Have you ever wanted to call a member function in your class, but not
known what it will be at compile time? I’m writing a SAX parser and
would like a function for every element name. I could write a massive
switch statement in the startElement function, but this will
quite quickly become unmanagable for a large schema. The alternative is
to look to see if a particular member function exists and call it.
To do this little bit of magic we need to use Java’s introspection API. The
first thing to do is to get a Class object for our class. We
can do that by calling:
Class klass = this.getClass();
We can then look up the method we are looking for using
Class.getMethod, but this function requires an array of types
that the method we are looking for takes as parameters, so we get the
right version of an overloaded method. We can do this with:
Class[] arguments = { Int.class, String.class, URL.class};
Method method = klass.getMethod("foo", arguments);
Now we have our method, we can call it using the
Method.invoke() call. This takes an object as the first
parameter, which we can use this, and an array of
Objects for the parameters.
Object[] values = {bar, baz, quux};
method.invoke(this, values);
But what happens if our class has no member method called
foo()? Well, Class.getMethod() will throw a
NoSuchMethodException, so we can just throw a
try/catch block around the code to deal with unhandled
functions. It’s worth pointing out that Class.getMethod() also
throws SecurityException and Method.invoke() throws
IllegalAccessException, IllegalArgumentException and
InvocationTargetException, so you’ll want to catch
Exception too.
We can chain some of these calls together and the result for my SAX
parser is:
public void startElement(String uri, String localName, String qName, Attributes atts)
throws SAXException {
try {
Class[] argTypes = { String.class, String.class, String.class,
Attributes.class };
Object[] values = { uri, localName, qName, atts };
this.getClass().getMethod("startElement_" + localName, argTypes)
.invoke(this, values);
} catch (NoSuchMethodException e) {
log.debug("unhandled element " + localName);
} catch (Exception e) {
e.printStackTrace();
}
}
With this arrangement, when I want to handle a new element in my code I
can just make a function like:
public void startElement_foo(String uri, String localName, String qName, Attributes atts)
throws SAXException {
...
}