Your browser may have trouble rendering this page. See supported browsers for more information.

This page shows the source for this entry, with WebCore formatting language tags and attributes highlighted.

Title

Finding <i>Conforming</i> Methods

Description

<n>This article was originally published on the <a href="http://blogs.encodo.ch/news/view_article.php?id=6"><b>Encodo Blogs</b></a>. Browse on over to see more!</n> <hr> <n>This is a two part post illustrating some tricks for working with the Java reflection API. <a href="{app}view_article.php?id=1422">Part two</a> is available here.</n> Java reflection provides a wealth of information about your code. One interesting use of this information is to layer scriptability on top of an application, calling code dynamically. Suppose we wanted to do the following: <ol> Given an object, a method name and a list of parameters, execute the matching method on the given object. Determine from the object's class whether the given method can be executed from the given context (web, command-line, etc.) </ol> Let's tackle step one first: a logical approach is to get the <c>Class</c> for the target object and call <c>getMethod()</c> with the method name and list of parameters to get the desired <c>Method</c> object. Sounds pretty easy, right? The Java reflection API puts a few stumbling blocks in the way. <h>Conforming, not Exact</h> <c>getMethod()</c> finds only methods whose parameter lists <i>are an exact match</i> for the one given, not methods, which can actually be called with that list of parameters. That is, it ignores polymorphism completely when performing a search. For the following discussion, assume the following definitions: <code> public class Person { public void executeCommand(String s) { } @Callable(CallLocation.FromWeb)<fn> public void giveCommandTo(String s, Person p) { p.ExecuteCommand(s); } } public class Assistant extends Person { } public class Manager extends Person { List<person> underlings = new ArrayList<person>(); public boolean getIsInChainOfCommand(Person p) { return underlings.contains(p); } public void giveCommandTo(String s, Person p) { if (! getIsInChainOfCommand(p)) { throw new RuntimeException("Cannot order this person around."); } p.ExecuteCommand(s); } } </code> Managers can only order their own underlings around. We expect to be able to call <c>giveCommandTo()</c> with a piece of text and an <c>Assistant</c>, and Java---polymorphic wunderkind that it is---obliges. As mentioned above, <c>getMethod("giveCommandTo", {new String(), new Assistant()})</c><fn> returns <c>null</c> because <c>Assistant</c>, though a conforming actual parameter, is not an exact match for the formal parameter. That's a shame. I'm sure <c>getMethod()</c> is much faster for this optimization, but it doesn't really work for applications that would like to benefit from polymorphism. Any application that wants to search for methods that can actually be executed will have to do so itself. In English, we want to get the list of methods on a class and iterate them until the name matches the desired method. If the matching method has the same number of formal parameters as actual parameters and each of the formal parameter types <c>isAssignableFrom</c> the corresponding actual parameter, we have a winner. The code below does this:<fn> <code> protected Method getConformingMethod(String methodName, Object[] actualParameters, Class cls) { Method[] publicMethods = cls.getMethods(); Method m = null; int idxMethod = 0; while ((m == null) && (idxMethod < publicMethods.length)) { m = publicMethods[idxMethod]; if (m.getName().equals(methodName)) { Class[] formalParameters = m.getParameterTypes(); if (actualParameters.length == formalParameters.length) { int idxParam = 0; while ((m != null) && (idxParam < formalParameters.length)) { Class param = formalParameters[idxParam]; if (!param.isAssignableFrom(actualParameters[idxParam].getClass())) { m = null; } idxParam++; } } else { m = null; } } else { m = null; } idxMethod++; } return m; } </code> A call to <c>getConformingMethod("giveCommandTo", {new String(), new Assistant()})</c> returns a match where <c>getMethod()</c> did not. Now that we have our method, we can check whether it can be called or not. For this, we retrieve the annotations on it. Continue on to <a href="{app}view_article.php?id=12">part two</a>. <hr> <ft>This code assumes a <c>Callable</c> annotation, which takes an enumeration as an argument. It is discussed in more detail in <a href="{app}view_article.php?id=12">part two</a>.</ft> <ft>In most cases, the list of parameter objects will already be available. The manifest array declaration is simply to illustrate the actual parameters.</ft> <ft>As you can plainly see from the example, the author hates break statements and any other form of engineering multiple return points out of a function. The style takes a bit of getting used to.</ft>