Finding all classes implementing a specific interface

68,230

Solution 1

I had a similar need where I wanted to make sure that any classes created that implemented a certain interface were always truly serializable. I created a JavaClassFinder which walks through all directories in the classpath, and finds all classes assignable to the interface I cared about. Here is a code snippet:

public <T> List<Class<? extends T>> findAllMatchingTypes(Class<T> toFind) {
    foundClasses = new ArrayList<Class<?>>();
    List<Class<? extends T>> returnedClasses = new ArrayList<Class<? extends T>>();
    this.toFind = toFind;
    walkClassPath();
    for (Class<?> clazz : foundClasses) {
        returnedClasses.add((Class<? extends T>) clazz);
    }
    return returnedClasses;
}

I'm happy to share the code with you if it helps. The only draw back is that this will only handle .class files -- I didn't add the feature to unzip .jars and read class files from there. (But it wouldn't be a huge project to add that.)

UPDATE: I checked my source code for the above, and found it depends on a lot of helper classes in our standard utility library. To make it easier, I zipped up all the code needed, which you can download from JavaClassFinder.zip. This will set up directly in Eclipse, and you can take whatever portions of the code you need.

You will find a JUnit3 test in the project, called JavaClassFinderTest.java, which shows you the features and usage of the JavaClassFinder class. The only external dependency needed to run the Junit test is Junit.

Basic usage of this utility:

    JavaClassFinder classFinder = new JavaClassFinder();
    List<Class<? extends MyTagInterface>> classes = classFinder.findAllMatchingTypes(MyTagInterface.class);

This will give you a List which contains any classes in the classpath which are assignable from the "MyTagInterface.class" (for example). Hope this helps.

Solution 2

You can find an answer here.

I can suggest using org.reflections

You can take a look at it here

Reflections reflections = new Reflections("com.mycompany");    
Set<Class<? extends MyInterface>> classes = reflections.getSubTypesOf(MyInterface.class);

Solution 3

Probably the best (standard) way to do this by using the Java SPI mechanism, see Javadoc. The inconvenient (which is also a nice feature) is that it expects Jars that define extensions to list them in META-INF/services/your.fully.qualified.Interface.

The only other way I can think of would be to wall through all ClassLoaders in hope you'll be able to list the files in there, load the class files, and see if they implement your interface or not - which is not a nice thing to do.

Solution 4

I think that org.reflections is a proper solution as mentioned by Alex Stybaev, you don't need to reference the classes in a property file.

Another approach (which I would take) is Spring, since I am using Spring anyway for my applications and therefore wouldn't need any additionally dependencies.

You can find hints for solving your problem with Spring (and alternatives in the other comments or answers) here:

In the comments of your question heikkim and polypiel also link to questions with have answers for solving it with Spring:

Share:
68,230

Related videos on Youtube

Umesh Awasthi
Author by

Umesh Awasthi

Updated on July 09, 2022

Comments

  • Umesh Awasthi
    Umesh Awasthi almost 2 years

    I am in the process of developing an application (Quartz scheduler) where we have a job class which is responsible for actually executing the work and we need to tell/pass the name of the job class while creating a trigger in the Quartz scheduler.

    I want to provide an extension point to all who want to use the API (beside the some generic jobs which I will provide as part of the API). The idea is to create an (marker) interface and if any one wants to declare their class as scheduler job class, all they have to do is, to (declare to) implement the interface.

    I am not sure how I can find which classes are following the contract (by implementing the interface) so that I can show them to the user who want to schedule a trigger in the scheduler.

    My requirement is not to load the classes at run time but to show user list of classes which implement the required interface so that user can select the class and class name can be passed to the scheduler. It's the Quartz scheduler which at the end will be responsible to create an instance of class.

    Can any one suggest how I can achieve the above goal or is there any other better way to achieve what I am trying to do?

    Edit

    I went through the doc of ServiceLoader and it seems that for implementing a service one has to create a file in the META-INF folder with the name of the implementation class, which leads me to think that if the user of my API wants 20 different implementations, he has to put 20 entries in the file which for me seems a lot of extra work for the end user since each job class will be created for executing a specific job and there can be 100s of job classes.

    Please correct me if my assumption is wrong.

    • polypiel
      polypiel about 12 years
      Another try is to create an annotation and then searching for the annotated classes. Here stackoverflow.com/questions/259140/… says how to achieve it.
    • titogeo
      titogeo about 12 years
      Would this help? link
  • Umesh Awasthi
    Umesh Awasthi about 12 years
    thanks for the input, i was going through ServiceLocator document, it seems for each implimentation of my interface (service), end user has to put the entry in the file so that it can be loaded, is it like if the developer wants 20 such implimentation he/she need to put all those implimentation name in the file, seems a lot of work for end user.
  • Yves Martin
    Yves Martin about 12 years
    The "walkClassPath" method is lacking. May you provide it please ?
  • Sam Goldberg
    Sam Goldberg about 12 years
    @YvesMartin: Yves, I updated my answer to include all link to download the original code. It may be more than you wanted, but it is a complete working version.
  • shashankaholic
    shashankaholic about 12 years
    @SamGoldberg Excellent utility you have designed here.
  • Sam Goldberg
    Sam Goldberg about 12 years
    @shashankaholic: thanks - I'm interested to know if you get any use out of it. We use it as part of our unit test suite to test that all subclasses of a particular type are truly serializable. (I found that just implementing Serializable doesn't guarantee that instances of a class can be serialized successfully.)
  • Umesh Awasthi
    Umesh Awasthi almost 12 years
    @SamGoldberg: i used your utility and its working perfectly fine except in web application.i saw this code if (System.getProperties().containsKey(CUSTOM_CLASS_PATH_PROPER‌​TY)) { // LOG.debug("getClassPathRoots(): using custom classpath property to search for classes"); classPath = System.getProperty(CUSTOM_CLASS_PATH_PROPERTY); } else { classPath = System.getProperty(JAVA_CLASS_PATH_PROPERTY); } in JavaClassFinder class and seems like its classpath its picking is 'bootstrap.jar' for tomcat.I am wondering is there way to use this utility in web application
  • Sam Goldberg
    Sam Goldberg almost 12 years
    @user577691: I will take a look at it. The custom classpath property is only there to be used if you need to specify a different classpath to search than the one your app is using to run. So if you set that System property, it will override the application classpath. Also, can you clarify your question: is the problem you saw that it only picked up the Tomcat classpath and not the web app classpath?
  • PJP
    PJP over 2 years