我正在为大学项目开发一个匹配工具,这个工具主要由多个简单的匹配算法组成。现在我想让人们能够轻松地添加他们自己的算法,并且只需将.java文件添加到项目文件夹中,就能自动将这些算法包含到集合中。
每个算法必须实现MatchingComponentInterface接口,我希望实现这个接口的每个类都能通知集合它的存在,这样集合就能动态地组装自己,包括那个算法。
为了简化起见,让我们假设主程序和集合代码如下所示:
class Main { @Getter private Ensemble ensemble = new Ensemble(); public static void main(String[] args){ //SomeMatchingComponent.touch(); Result res = ensemble.match(args[0], args[1]); }}
请注意被注释掉的touch()调用,我稍后会回到这个话题。
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)); }); }}
此外,我可能有几个MatchingComponent实现,看起来大致如下:
public class SomeMatchingComponent implements MatchingComponentInterface{ //这只会在代码中第一次提及类时执行 //然而我想在不提及类的情况下执行它 static { MatchingComponent foo = new MatchingComponent(); Main.getEnsemble().addComponent(foo); } //static void touch(){} public Result match(Object first, Object second){ //匹配魔法 }}
现在看看静态代码块。只要我在代码的其他地方使用这个类,这个代码就会执行。然而在这个例子中,这不会发生,因为我也注释掉了touch()
方法以及主方法中的调用。
当集合被构建时,主方法需要预先知道所有组件,以便触及并将它们添加到集合中。然而我想在没有任何这些操作的情况下添加它们。它们应该自动添加自己。
我的问题是:有没有一种方法可以强制执行静态代码块,而不需要硬编码哪些组件存在,或者让接口调用自己所有实现的方法呢?
回答:
实际上,我已经找到了一个编程解决方案。使用reflections库,可以检测任何类或接口的所有子类或实现,因此通过一些这样的代码,我可以实现我需要的功能:
public void addAllComponents(Ensemble ensemble){ //"matching.component"是一个前缀,因为所有组件都在一个以此命名的包中 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) { //适当处理异常 } });}
我是在这里的一个非常旧的问题中找到这个库的: