0

I am building a matching tool in Java for a university project which basically just consists of an ensemble of several simpler matching algorithms. Now I want people to be able to easily add their own algorithms and automatically include them into the ensemble by just adding the .java files into the project folder.

Each algorithm must implements a MatchingComponentInterface and I want to achieve that each class implementing this interface tells the ensemble that it exists so that the ensemble can dynamically assemble itself including that algorithm.

For a simplified example let the main and ensemble code look like this:

class Main {
  @Getter
  private Ensemble ensemble = new Ensemble();

  public static void main(String[] args){
    //SomeMatchingComponent.touch();
    Result res = ensemble.match(args[0], args[1]);
  }
}

Notice the commented touch() call, I will get back to this later.

public class Ensemble{

  private List<MatchingComponentInterface> components;
  private Result combinedResult = new Result();

  public void addComponent(MatchingComponentInterface component){
    components.add(component);
  }

  public void match(Object first, Object second){
    components.forEach(component -> {
      combinedResult.addResult(component.match(first, second));
    });
  }
}

Furthermore I might have several MatchingComponent implementations looking roughly like this:

public class SomeMatchingComponent implements MatchingComponentInterface{

  //this is only executed the first time the class is mentioned in the code
  //however I want this do be executed without mentioning the class
  static {
    MatchingComponent foo = new MatchingComponent();
    Main.getEnsemble().addComponent(foo);
  }

  //static void touch(){}

  public Result match(Object first, Object second){
    //matching magic
  }
}

Now take a look at the static code bock. This code is executed as soon as I use the class somewhere else in the code. However in this example this won't happen as I commented out the touch() method as well as the call in the main method.

When the ensemble is built the main method needs to know all components beforehand in order to to touch and add them to the ensemble. However I want to add them without any of that. They should add themselves.

My question now is: Is there a way to force execute the static code block anyway without hard coding which components exist or maybe let the interface call all implementations of itself?

Mir4culix
  • 81
  • 7
  • Maybe someone else has a better answer, but if all of your MatchingComponentInterface classes are in the same directory, on startup, you can read the .java files in that directory and get the class names from the file names. – Gilbert Le Blanc Feb 12 '21 at 14:32
  • Thanks Gilbert! I actually had this idea but I wanted to avoid it and do it programmatically. I found a way though and if you're interested, check my answer. – Mir4culix Feb 16 '21 at 14:12
  • @GilbertLeBlanc tagging you because I didn't in my previous comment. – Mir4culix Feb 16 '21 at 14:37

1 Answers1

0

I have actually found a solution to solve that programatically. Using the reflections library it is possible to detect all sub classes of any class or implementations of any interface, so with a bit of code like this I can achieve what I need:

public void addAllComponents(Ensemble ensemble){

  //"matching.component" is a prefix as all components are in a package with that name
  Reflections reflections = new Reflections("matching.component");
  Set<Class<? extends MatchingComponentInterface>> classes 
    = reflections.getSubTypesOf(MatchingComponentInterface.class);
  
  classes.forEach(aClass -> {
    try{
      ensemble.addComponent(aClass.getConstructor().newInstance());
    } 
    catch (NoSuchMethodException | IllegalAccessException | 
          InstantiationException | InvocationTargetException e) {
      //Handle exceptions appropriately
    }
  });
}

I found this library in a really old question here:

How can I get a list of all the implementations of an interface programmatically in Java?

Mir4culix
  • 81
  • 7