Search code examples
javajava-8static-analysisfunctional-interface

How can I search a java code base for interfaces that have a single method?


A project I work on has recently switched from Java 7 to Java 8. I'd like to be able to find interfaces that have a single abstract method as candidates for introducing functional interfaces into our code base. (Annotating existing interfaces as @FunctionalInterface, extending them from the interfaces in java.util.function, or possibly just replacing them).


Solution

  • The reflections project is able to locate and return all classes on the classpath. Here's a working example:

    ReflectionUtils.forNames(new Reflections(new ConfigurationBuilder().setScanners(new SubTypesScanner(false))
                                                                       .addUrls(ClasspathHelper.forClassLoader()))
                             .getAllTypes()).stream()
                   .filter(Class::isInterface)
                   .collect(toMap(c -> c,
                                  c -> Arrays.stream(c.getMethods())
                                             .filter(m -> !m.isDefault())
                                             .filter(m -> !Modifier.isStatic(m.getModifiers()))
                                             .filter(m -> !isObjectMethod(m))
                                             .collect(toSet())))
                   .entrySet().stream()
                   .filter(e -> e.getValue().size() == 1)
                   .sorted(comparing(e -> e.getKey().toString()))
                   .map(e -> e.getKey().toString() + " has single method " + e.getValue())//getOnlyElement(e.getValue()))
                   .forEachOrdered(System.out::println);
    

    The isObjectMethod helper is defined like this:

    private static final Set<Method> OBJECT_METHODS = ImmutableSet.copyOf(Object.class.getMethods());
    private static boolean isObjectMethod(Method m){
        return OBJECT_METHODS.stream()
                             .anyMatch(om -> m.getName().equals(om.getName()) &&
                                             m.getReturnType().equals(om.getReturnType()) &&
                                             Arrays.equals(m.getParameterTypes(),
                                                           om.getParameterTypes()));
    }
    

    This doesn't help you go back to the source code and add the annotations, but it'll give you a list to work from.

    As requested in the comments, the imports needed to make this work are:

    import static java.util.Comparator.*;
    import static java.util.stream.Collectors.*;
    
    import java.lang.reflect.Method;
    import java.lang.reflect.Modifier;
    import java.util.Arrays;
    import java.util.Set;
    
    import org.reflections.ReflectionUtils;
    import org.reflections.Reflections;
    import org.reflections.scanners.SubTypesScanner;
    import org.reflections.util.ClasspathHelper;
    import org.reflections.util.ConfigurationBuilder;
    
    import com.google.common.collect.ImmutableSet;