If you have an interface or abstract class defined in some jar
package whatever.project;
public interface DoesThing {
void doThing();
}And in another jar you have one or more implementations of that interface which has a zero argument constructor
package something.other;
public final class DoesThingImpl implements DoesThing {
@Override
public void doThing() {
System.out.println("I implemented this in a certain way");
}
}as well as a file in that jar with the interface name under META-INF/services
META-INF/services/whatever.project,DoesThing
which has a line that has the name of the implementing class
something.other.DoesThingImpl
Then you can obtain an implementation of that interface via the service loader mechanism
var loader = ServiceLoader.load(DoesThing.class);
for (var thingDoer : loader) {
thingDoer.doThing();
}This is some really core magic and is how most of the projects that want you to just add dependencies to get functionality - like slf4j, jdbc, twelvemonkeys, etc - use to do their thing