Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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).

like image 831
smithkm Avatar asked Jan 08 '16 23:01

smithkm


1 Answers

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;
like image 194
Matt McHenry Avatar answered Oct 06 '22 00:10

Matt McHenry