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

|<<>>|234 of 273 Show listMobile Mode

Finding Conforming Methods

Published by marco on

This article was originally published on the Encodo Blogs. Browse on over to see more!


This is a two part post illustrating some tricks for working with the Java reflection API. Part two is available here.

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:

  1. Given an object, a method name and a list of parameters, execute the matching method on the given object.
  2. Determine from the object’s class whether the given method can be executed from the given context (web, command-line, etc.)

Let’s tackle step one first: a logical approach is to get the Class for the target object and call getMethod() with the method name and list of parameters to get the desired Method object.

Sounds pretty easy, right? The Java reflection API puts a few stumbling blocks in the way.

Conforming, not Exact

getMethod() finds only methods whose parameter lists are an exact match 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:

public class Person {
  public void executeCommand(String s) {
  }

  @Callable(CallLocation.FromWeb)[1]
  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);
  }
}

Managers can only order their own underlings around. We expect to be able to call giveCommandTo() with a piece of text and an Assistant, and Java—polymorphic wunderkind that it is—obliges. As mentioned above, getMethod(“giveCommandTo”, {new String(), new Assistant()})[2] returns null because Assistant, though a conforming actual parameter, is not an exact match for the formal parameter.

That’s a shame. I’m sure getMethod() 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 isAssignableFrom the corresponding actual parameter, we have a winner. The code below does this:[3]

  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;
  }

A call to getConformingMethod(“giveCommandTo”, {new String(), new Assistant()}) returns a match where getMethod() 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 part two.


[1] This code assumes a Callable annotation, which takes an enumeration as an argument. It is discussed in more detail in part two.
[2] In most cases, the list of parameter objects will already be available. The manifest array declaration is simply to illustrate the actual parameters.
[3] 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.