Java: How to get all implementations of an Interface

Frankie
3 min readDec 28, 2022

--

How to list all known implementations of an interface is something that I’m asked a lot. I’m biased towards two ways of doing it.

Java 6 brought us the Service Provider Interface (SPI) which has at its core the ServiceLoader class, responsible for loading implementations.

Java itself uses the Service Provider Interface (SPI) in many ways:

Using it is straightforward. You just have to declare all providers in a provider configuration file. In order to do so, put them in a META-INF/services/com.example.providers.CustomProvider and the content of this file will list the existing providers:

com.example.providers.ProviderOfFood
com.example.providers.ProviderOfHappiness

File contents are the fully qualified name(s) of the implementations.

I don’t really like the above solution, it’s brittle and prone to errors. I’ve seen programs where providers were not available as there was a typo, and I’ve also seen lost hours because developers refactored classes forgetting to update the respective META-INF files.

Both libraries below achieve the same.

Say you have the following code:

public interface Provider {
void sayHello();
}

@AutoService(Provider.class)
public class ProviderOfFood implements Provider {
...
}

@AutoService(Provider.class)
public class ProviderOfHappiness implements Provider {
...
}

Two implementations of the hypothetical Provider interface

Google AutoService

At compile time, Google AutoService searches your code for annotations of type @AutoService and automatically writes the META-INF file for you. This implementation, having the search at compile time, is the fastest one. You would call it like this:

ServiceLoader<Provider> providers = ServiceLoader.load(Provider.class);
for (Provider p : providers) {
p.sayHello();
}

You simply use Java’s ServiceLoader to get your implementations

Ronmano Reflections

Finds your classes at runtime using reflections. To make faster, you must narrow down the search scope by providing some package info to the library. This implementation has the benefit of working better with hot-reloaded code. Though you’ll pay the price in speed. You would call it like this:

Reflections reflections = new Reflections("com.example.providers");
Set<Class<?>> subTypes = reflections.get(SubTypes.of(Provider.class).asClass());

for (Class<?> aClass : subTypes) {
Constructor<?> ctor = aClass.getConstructor();
Provider p = (Provider) ctor.newInstance();
p.sayHello();
}

Notice that you specified com.example.providers as the location to search for providers

Which library should we use?

Depends.

ServiceLoader is 10x faster than Ronmano Reflections, but even on very modest hardware, running Ronmano Reflections 10_000 times will only take you back an extra ~8 seconds.

When you use ServiceLoader you initialize the classes. If you just want to list them, you can read the META-INF file.

Using Ronmano Reflections tough, you get the list of the classes, and you have to initialize them by hand. That, depending on the situation, may be a plus.

I’m a fan of mechanical keyboards, but this season gifted some LogicTech K120 to family and friends who I knew had outrageously worn out keyboards. Huge success! If I can’t offer a perishable item, I try to make it something that wears out. I’ve learnt my lesson on decorative articles a long time ago! ;)

Originally published at https://wasteofserver.com on December 28, 2022.

--

--

No responses yet