samedi 29 février 2020

Compiler-like Java Reflection method resolution

I'm programming a method that uses Reflection to find methods the "same way" (not exactly, as you will see) the compiler does. I left Generics out of the scope in this case, as the type parameter does not affect signature of methods (as far as I know).

Basically I want to ask if there's a baked library that already does this and how far are my methods from accomplishing the task in a relatively acceptable way (I don't expect perfect and full resolution, I don't even need static methods).

I could not find the exact spects about how does the compiler actually perform the task, so I made a guess that suits the needs of my environment, however, if possible and not too complex, I'd like to have it done in the proper way for possible complex future uses.

Right now, my assumption is IGNORING overloaded methods with different return types, as it is not a problem with my current problem, but that may be in the future, so I may be interested in taking it into consideration if you have ideas.

So, as I don't have to care about return types, I start by calling a simple

clazz.getMethods();

and then I perform a filter by name. I'm aware I'm totally missing the overrides mentioned abvove here. This is a first approach.

This is how I calculate the distance between a method and the desired arguments: If parent and children are the same instance, then the distance is 0. If not, this methods calls classDistance recursively on children superclass and all directly implemented interfaces. The distance will be the smalles positive distance plus one. (ignoring incompatible ancestors). This solution works for me because right now all the functions I need to call have just one parameter, and the few that have a second one, allways do a perfect match on first parameter on the desired method, so there's only one positive distance to narrow down.

Actual code:

private static Method getBestMatch(List<Method> validMethods, List<Class<?>> classes) {
    if(validMethods.size() == 1) return validMethods.get(0);
    int distance = Integer.MAX_VALUE;
    Method currMethod = null;
    outer_loop:
    for(Method method : validMethods) {
        Class<?>[] methodTypes = method.getParameterTypes();
        int methodDistance = 0;
        for(int i=0; i < methodTypes.length; i++) {
            if(!methodTypes[i].isAssignableFrom(classes.get(i))) continue outer_loop; // Incompatible. Should not happen, but just in case
            methodDistance += classDistance(methodTypes[i], classes.get(i));

        }
        if(currMethod == null || methodDistance < distance) {
            currMethod = method;
            distance = methodDistance;
        }
    }
    return currMethod;
}

Distance calculator:

private static int classDistance(Class<?> parent, Class<?> children) throws IllegalArgumentException{
    if(parent.equals(children)) return 0;
    if(!parent.isAssignableFrom(children)) throw new IllegalArgumentException("children is not assignable to father"); // Should do b4 equals?
    Integer minDistance = null;

    Class<?> superClass = children.getSuperclass();
    if(superClass != null && parent.isAssignableFrom(superClass)) {
        minDistance = classDistance(parent, superClass);
    }
    for(Class<?> directInterface : children.getInterfaces()) {
        if(!parent.isAssignableFrom(directInterface)) continue;
        int interfaceDistance = classDistance(parent, directInterface);
        if(minDistance == null || interfaceDistance < minDistance)  minDistance = interfaceDistance;
    }

    if(minDistance == null) throw new IllegalArgumentException("we found no distance. this is an odd behaviour and definetly a bug, or means this method is not well-thought at all");
    return minDistance + 1;
}

Things I should take into consideration:

  • Should I complain about ambigous methods? As you can see I just pick up the first
  • That overloaded with different type issue... How can I know precedence? If I'm not wrong, compiler allways picks the method of the closest class.
  • That distance calculation looks too simple to be true




Aucun commentaire:

Enregistrer un commentaire