Question

I am implementing an IdChecker program who check the uniqueness of "id's" for classes and methods of a project. The IdChecker run only at compilation of my project...before I push a release.

For the classes it is easy to get the id's because I just need to map all my classes then invoke MyClass.getField("CLASS_ID").getShort(MyClass.getField("CLASS_ID")); then I create a ClassWrapper who contain the className and the classIdentifier.

For the methods, it's a little bit more complicated, because I can't use reflection to access a variable who is local to a method...but...in my project, I use a debuglogger to log every "entering-the-method" and "leaving-the-method". To get the local variable I want to invoke a method then read in my logBuffer the last entry (who is the "leaving-the-method" log entry) from this log entry, I get the METHOD_ID.

My problem:

I can't invoke the method on the class it generate an IllegalArgumentException... I don't need to pass parameters to the method, I just want to invoke them that I can generate a log entry.

private void getMethodsList() throws IllegalArgumentException,
    IllegalAccessException, NoSuchFieldException, SecurityException   {
    final short METHOD_ID = 0x03;
    /* Log-entering the method */
    mLogger.logDebug((byte) 1, METHOD_ID);

    /* Create the MethodWrapper list corresponding
       to each element of the ClassWrapper list */
    for(Class<?> clazz : mClasses)
    {
        /* Get the declared methods from each class */
        for(Method method : clazz.getDeclaredMethods())
        {
            /* Get the name of the method */
            String newName = method.getName();
            short newIdentifier = 0;
            try
            {
                method.invoke(clazz, new Object[]{null});
                /* Get the identifier of the class */
                newIdentifier = AbstractDebugLogger.mLastMethodID;
            }

        .
        .
        .
Was it helpful?

Solution

method.invoke(...) requires an array of arguments that could actually be used to call the method. In this case, you give it {null}, which works for any function that accepts a single Object, but no other functions. To call arbitrary functions, you need to match the length of the parameter list to the number of arguments the function needs:

method.invoke(..., new Object[method.getParameterTypes().length])

This still has a few problems. First, the first parameter of invoke is not the class as you entered it, but instead an instance of an object of that class (or null, for static methods). You would need to search through the class's constructors and construct an instance of that class before you could call instance methods of that class. The second problem is that this does not work with functions that require primitives, which cannot be null. You could check the classes of the parameters and replace null with 0 or false as appropriate. The third, most important problem, which is the reason that it is so difficult to do this, is that you do not actually want to invoke the method. You want to retrieve information about the method, which in this case happens to be stored inside the method. The proper solution is to just store the information attached to the method, with an annotation:

@Retention(RetentionPolicy.RUNTIME)
@interface ID {
    int value();
}
...
@ID(1337)
void exampleMethod() { ... }
...
newIdentifier = method.getAnnotation(ID.class).value(); //returns 1337 for exampleMethod
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top