Find all classes A that implement B and aggregate both A and T to build a map out of them In my project I have the following base interfaces used through the application:

interface Node { /* ... */ }
interface NodeService&lt;T&gt; { /* ... */ }


Concrete classes might look as follows:

class User implements Node { /* ... */ }
class UserService implements NodeService&lt;User&gt; { /* ... */ }

class Entity implements Node { /* ... */ }
class EntityService implements NodeService&lt;Entity&gt; { /* ... */ }


In the end I want to have a Map&lt;String, Class&lt;? extends NodeService&lt;Node&gt;&gt;&gt; that in the case above might look as follows:

Map{
    User=UserService.class,
    Entity=EntityService.class
}


My current solution has the following approach:


Using the Reflections library find all classes implementing NodeService
Iterate over all generic interfaces of said classes until we find the NodeService interface
Access its first and single generic type argument
Cast the type to a class and map it to its simple name
Build a tuple (using vavr in this case) containing the simple name and the service class we found in step 1
Flatten the result and collect the tuples into a map


@SuppressWarnings({"unchecked", "rawtypes"})
final Map&lt;String, Class&lt;? extends NodeService&lt;Node&gt;&gt;&gt; nodeServices =
    new Reflections(BackendApplication.class.getPackageName())
        .getSubTypesOf(NodeService.class).stream()
            .map(
                serviceClass -&gt;
                    Stream.of(serviceClass.getGenericInterfaces())
                        .filter(ParameterizedType.class::isInstance)
                        .map(ParameterizedType.class::cast)
                        .filter(type -&gt; type.getRawType().equals(NodeService.class))
                        .map(ParameterizedType::getActualTypeArguments)
                        .map(typeArguments -&gt; typeArguments[0])
                        .filter(Class.class::isInstance)
                        .map(Class.class::cast)
                        .map(Class::getSimpleName)
                        .map(nodeClass -&gt; Tuple.of(nodeClass, (Class) serviceClass))
                        .collect(Collectors.toSet()))
            .flatMap(Collection::stream)
            .collect(Collectors.toMap(Tuple2::_1, Tuple2::_2));


I am surprised that this works in the first place but the code is ridiculously complex and I am looking for suggestions on how to rewrite this. Some of my biggest pain points are the handling of the classes generic interfaces even though in the end I'm only interested in a single one of them making the stream in the middle introduce unnecessary nesting. Furthermore the way I attempt to navigate to the correct type is really bothering me as well.

Thus I am open for your input on how to improve this chunk of code.

The performance of this snippet is surprisingly good. I am mainly looking for input on the readability and refactoring this to make it much more maintainable, even at the cost of performance since this will be only invoked once during the application lifecycle.
Should we expect that there are services that have a different type parameter? Because otherwise just a Set of services may suffice, or you can simply remove the Service part of the name during the mapping. This is an obvious thing from the example, but I'm not sure how far the example matches your actual code.

What I often see when streams are used is that suddenly the split up into methods is forsaken. For instance, in my code I would first build a list of services and then create a map out of it. I don't think that an intermediate list would do much w.r.t. performance here (the number of services will be limited, loading the classes will take much more time, and the streaming will have intermediate representations as well), and the code would clearly be simplified. Method chaining can be nice, but please don't overdo it.

Similarly, a method that tests if a class implements an interface can be clearly split out:

Stream.of(serviceClass.getGenericInterfaces())
    .filter(ParameterizedType.class::isInstance)
    .map(ParameterizedType.class::cast)
    .filter(type -&gt; type.getRawType().equals(NodeService.class))


And actually, if you do that and look for code you will find NodeService.class.isAssignableFrom(clazz). The part is well programmed but please do use library functions when they are available.

All of above may not do much with regards to performance, but taking it into account should definitely help with the readability. Currently you had to describe what the code does in the question description. That should not be necessary for well programmed / documented code.