237

How does one go about and try to find all subclasses of a given class (or all implementors of a given interface) in Java? As of now, I have a method to do this, but I find it quite inefficient (to say the least). The method is:

  1. Get a list of all class names that exist on the class path
  2. Load each class and test to see if it is a subclass or implementor of the desired class or interface

In Eclipse, there is a nice feature called the Type Hierarchy that manages to show this quite efficiently. How does one go about and do it programmatically?

guerda
  • 23,388
  • 27
  • 97
  • 146
Avrom
  • 4,967
  • 2
  • 28
  • 35
  • 2
    Although the solution based on the Reflections and Spring look interesting, I needed some simple solution that didn't have dependancies. It seems that my original code (with some tweaks) was the way to go. – Avrom Feb 03 '09 at 14:39
  • 1
    Surely you can use the getSupeClass method recursively? –  Apr 25 '11 at 12:35
  • I was specifically looking for *all subclasses* of a given class. getSuperClass will not tell you what subclasses a class has, only get the immediate super class for a specific subclass. Also, the method isAssignableFrom on Class is better suited for what you suggest (no need for recursion). – Avrom May 02 '11 at 17:48
  • 2
    This question is linked from many other duplicates, but it doesn't contain any useful, plain Java answer. Sigh... – Eric Duminil Apr 28 '20 at 20:01
  • @EricDuminil See Lyfing answer. Plain Java, constrained search. Just what I wanted. – Art Swri Jul 11 '22 at 14:24

19 Answers19

148

Scanning for classes is not easy with pure Java.

The spring framework offers a class called ClassPathScanningCandidateComponentProvider that can do what you need. The following example would find all subclasses of MyClass in the package org.example.package

ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);
provider.addIncludeFilter(new AssignableTypeFilter(MyClass.class));

// scan in org.example.package
Set<BeanDefinition> components = provider.findCandidateComponents("org/example/package");
for (BeanDefinition component : components)
{
    Class cls = Class.forName(component.getBeanClassName());
    // use class cls found
}

This method has the additional benefit of using a bytecode analyzer to find the candidates which means it will not load all classes it scans.

fforw
  • 5,391
  • 1
  • 18
  • 17
  • 26
    False should be passed in as the parameter when creating ClassPathScanningCandidateComponentProvider to disable the default filters. The default filters will match other types of classes, e.g. anything annotated with @Component. We only want the AssignableTypeFilter to be active here. – MCDS Jan 31 '13 at 00:07
  • 1
    You say it's not easy, but how would we do it if we wanted to do it with pure java? – Aequitas Sep 03 '15 at 01:49
80

There is no other way to do it other than what you described. Think about it - how can anyone know what classes extend ClassX without scanning each class on the classpath?

Eclipse can only tell you about the super and subclasses in what seems to be an "efficient" amount of time because it already has all of the type data loaded at the point where you press the "Display in Type Hierarchy" button (since it is constantly compiling your classes, knows about everything on the classpath, etc).

matt b
  • 138,234
  • 66
  • 282
  • 345
  • 32
    There is now a simple library called [org.reflections](https://github.com/ronmamo/reflections) that helps with this and other common reflection tasks. With this library you can just call `reflections.getSubTypesOf(aClazz))` [link](https://github.com/ronmamo/reflections) – Enwired Feb 20 '15 at 01:29
  • @matt b - if it has to scan all the classes, does this means that there is a performance degradation when you have a lot of classes in your project, even though only few of them are sub-classing your class ? – LeTex Sep 24 '15 at 15:00
  • 1
    Exactly. It just touches all classes. You can define your own scanner thou you can speed it up like excluding certain packages you know your class would not be extended or just open the class file and check for finding the class name in the class' constant section avoiding letting the reflection scanner read a lot more information on classes that do not even contain the required reference to your (direct) super class Indirectly you need to scan further. So its the best that there is at the moment. – Martin Kersten Jan 05 '16 at 13:52
  • The answer by fforw worked for me and should be marked as the correct answer. Clearly this is possible with classpath scanning. – Farrukh Najmi Mar 23 '17 at 12:50
  • 1
    You're wrong, see the response below about Burningwave library –  Dec 18 '19 at 09:54
  • They're right, whether or not there is a library to do it. What do you think the library does / uses? – Matthew Read Feb 16 '23 at 21:01
54

This is not possible to do using only the built-in Java Reflections API.

A project exists that does the necessary scanning and indexing of your classpath so you can get access this information...

Reflections

A Java runtime metadata analysis, in the spirit of Scannotations

Reflections scans your classpath, indexes the metadata, allows you to query it on runtime and may save and collect that information for many modules within your project.

Using Reflections you can query your metadata for:

  • get all subtypes of some type
  • get all types annotated with some annotation
  • get all types annotated with some annotation, including annotation parameters matching
  • get all methods annotated with some

(disclaimer: I have not used it, but the project's description seems to be an exact fit for your needs.)

Community
  • 1
  • 1
Mark Renouf
  • 30,697
  • 19
  • 94
  • 123
  • 2
    Interesting. The project appears to have some dependancies which their documentation doesn't seem to mention. Namely (the ones I found so far): javaassist, log4J, XStream – Avrom Jan 29 '09 at 16:51
  • 3
    I included this projekt with maven and it worked fine. Getting subclasses is actually the first source code example and is two lines long :-) – KarlsFriend May 08 '12 at 12:16
  • 1
    Is it not possible using only the built-in Java Reflections API or just very inconvenient to do so? – Flow Mar 10 '15 at 07:46
  • 1
    Please be careful when you are going to use Reflections and then deploy your app WAR to GlassFish! There is a conflict in Guava library and deployment will fail with error **CDI deployment failure:WELD-001408** - please see [GLASSFISH-20579](https://java.net/jira/browse/GLASSFISH-20579) for more details. [FastClasspathScanner](https://github.com/lukehutch/fast-classpath-scanner) is a solution in this case. – lu_ko May 19 '16 at 14:04
  • I just try this project and it works. I just use it to enhance the strategy design pattern and get all the strategy concrete class(subclass) I will share the demo latter. – Xin Meng Feb 02 '17 at 17:01
  • As stated before, think twice before depending on google reflections. It's not stable because it depends on guava which does not manage versions very well. https://github.com/ronmamo/reflections/issues/194 – Bart Swennenhuis Jun 13 '18 at 10:16
  • Is there a good alternative to Reflections? It uses WTFPL and lacks WTFPL headers in the source files, so our project can't use it. – berezovskyi Aug 07 '18 at 12:25
  • 1
    org.reflections is not maintained very well (no regular releases), lots of open issues and pull requests. Better use ClassGraph or Springs ClassPathScanningCandidateComponentProvider – xtermi2 Oct 15 '20 at 15:25
18

Try ClassGraph. (Disclaimer, I am the author). ClassGraph supports scanning for subclasses of a given class, either at runtime or at build time, but also much more. ClassGraph can build an abstract representation of the entire class graph (all classes, annotations, methods, method parameters, and fields) in memory, for all classes on the classpath, or for classes in selected packages, and you can query this class graph however you want. ClassGraph supports more classpath specification mechanisms and classloaders than any other scanner, and also works seamlessly with the new JPMS module system, so if you base your code on ClassGraph, your code will be maximally portable. See the API here.

Luke Hutchison
  • 8,186
  • 2
  • 45
  • 40
12

Don't forget that the generated Javadoc for a class will include a list of known subclasses (and for interfaces, known implementing classes).

Rob
  • 47,999
  • 5
  • 74
  • 91
  • 3
    this is totally incorrect, super class should not depend on their sub classes,not even in javadoc or comment. – hunter Mar 28 '17 at 06:15
  • 1
    @hunter I disagree. It is totally correct that the JavaDoc includes a list of _known_ subclasses. Of course, "known" might not include the class you are looking for, but for some use cases it will suffice. – Qw3ry Jul 04 '19 at 08:46
  • And you might miss some classes in any case: I can load a new jar into the classpath (during runtime) and every detection that happened before will fail. – Qw3ry Jul 04 '19 at 08:47
9

I did this several years ago. The most reliable way to do this (i.e. with official Java APIs and no external dependencies) is to write a custom doclet to produce a list that can be read at runtime.

You can run it from the command line like this:

javadoc -d build -doclet com.example.ObjectListDoclet -sourcepath java/src -subpackages com.example

or run it from ant like this:

<javadoc sourcepath="${src}" packagenames="*" >
  <doclet name="com.example.ObjectListDoclet" path="${build}"/>
</javadoc>

Here's the basic code:

public final class ObjectListDoclet {
    public static final String TOP_CLASS_NAME =  "com.example.MyClass";        

    /** Doclet entry point. */
    public static boolean start(RootDoc root) throws Exception {
        try {
            ClassDoc topClassDoc = root.classNamed(TOP_CLASS_NAME);
            for (ClassDoc classDoc : root.classes()) {
                if (classDoc.subclassOf(topClassDoc)) {
                    System.out.println(classDoc);
                }
            }
            return true;
        }
        catch (Exception ex) {
            ex.printStackTrace();
            return false;
        }
    }
}

For simplicity, I've removed command line argument parsing and I'm writing to System.out rather than a file.

David Leppik
  • 3,194
  • 29
  • 18
9

I know I'm a few years late to this party, but I came across this question trying to solve the same problem. You can use Eclipse's internal searching programatically, if you're writing an Eclipse Plugin (and thus take advantage of their caching, etc), to find classes which implement an interface. Here's my (very rough) first cut:

  protected void listImplementingClasses( String iface ) throws CoreException
  {
    final IJavaProject project = <get your project here>;
    try
    {
      final IType ifaceType = project.findType( iface );
      final SearchPattern ifacePattern = SearchPattern.createPattern( ifaceType, IJavaSearchConstants.IMPLEMENTORS );
      final IJavaSearchScope scope = SearchEngine.createWorkspaceScope();
      final SearchEngine searchEngine = new SearchEngine();
      final LinkedList<SearchMatch> results = new LinkedList<SearchMatch>();
      searchEngine.search( ifacePattern, 
      new SearchParticipant[]{ SearchEngine.getDefaultSearchParticipant() }, scope, new SearchRequestor() {

        @Override
        public void acceptSearchMatch( SearchMatch match ) throws CoreException
        {
          results.add( match );
        }

      }, new IProgressMonitor() {

        @Override
        public void beginTask( String name, int totalWork )
        {
        }

        @Override
        public void done()
        {
          System.out.println( results );
        }

        @Override
        public void internalWorked( double work )
        {
        }

        @Override
        public boolean isCanceled()
        {
          return false;
        }

        @Override
        public void setCanceled( boolean value )
        {
        }

        @Override
        public void setTaskName( String name )
        {
        }

        @Override
        public void subTask( String name )
        {
        }

        @Override
        public void worked( int work )
        {
        }

      });

    } catch( JavaModelException e )
    {
      e.printStackTrace();
    }
  }

The first problem I see so far is that I'm only catching classes which directly implement the interface, not all their subclasses - but a little recursion never hurt anyone.

Curtis
  • 3,931
  • 1
  • 19
  • 26
  • 3
    OR, it turns out you don't have to make your own search. You can just get an ITypeHierarchy directly from your IType by calling .newTypeHierarchy() on it: http://dev.eclipse.org/newslists/news.eclipse.tools.jdt/msg05036.html – Curtis Apr 21 '11 at 16:23
  • I'm even later ... the OP merely mentioned Eclipse in that Eclipse does what he wants, his "programmatically" statement excludes Eclipse as part of a solution. – user3481644 Nov 19 '21 at 20:00
7

Keeping in mind the limitations mentioned in the other answers, you can also use openpojo's PojoClassFactory (available on Maven) in the following manner:

for(PojoClass pojoClass : PojoClassFactory.enumerateClassesByExtendingType(packageRoot, Superclass.class, null)) {
    System.out.println(pojoClass.getClazz());
}

Where packageRoot is the root String of the packages you wish to search in (e.g. "com.mycompany" or even just "com"), and Superclass is your supertype (this works on interfaces as well).

mikołak
  • 9,605
  • 1
  • 48
  • 70
5

Depending on your particular requirements, in some cases Java's service loader mechanism might achieve what you're after.

In short, it allows developers to explicitly declare that a class subclasses some other class (or implements some interface) by listing it in a file in the JAR/WAR file's META-INF/services directory. It can then be discovered using the java.util.ServiceLoader class which, when given a Class object, will generate instances of all the declared subclasses of that class (or, if the Class represents an interface, all the classes implementing that interface).

The main advantage of this approach is that there is no need to manually scan the entire classpath for subclasses - all the discovery logic is contained within the ServiceLoader class, and it only loads the classes explicitly declared in the META-INF/services directory (not every class on the classpath).

There are, however, some disadvantages:

  • It won't find all subclasses, only those that are explicitly declared. As such, if you need to truly find all subclasses, this approach may be insufficient.
  • It requires the developer to explicitly declare the class under the META-INF/services directory. This is an additional burden on the developer, and can be error-prone.
  • The ServiceLoader.iterator() generates subclass instances, not their Class objects. This causes two issues:
    • You don't get any say on how the subclasses are constructed - the no-arg constructor is used to create the instances.
    • As such, the subclasses must have a default constructor, or must explicity declare a no-arg constructor.

Apparently Java 9 will be addressing some of these shortcomings (in particular, the ones regarding instantiation of subclasses).

An Example

Suppose you're interested in finding classes that implement an interface com.example.Example:

package com.example;

public interface Example {
    public String getStr();
}

The class com.example.ExampleImpl implements that interface:

package com.example;

public class ExampleImpl implements Example {
    public String getStr() {
        return "ExampleImpl's string.";
    }
}

You would declare the class ExampleImpl is an implementation of Example by creating a file META-INF/services/com.example.Example containing the text com.example.ExampleImpl.

Then, you could obtain an instance of each implementation of Example (including an instance of ExampleImpl) as follows:

ServiceLoader<Example> loader = ServiceLoader.load(Example.class)
for (Example example : loader) {
    System.out.println(example.getStr());
}

// Prints "ExampleImpl's string.", plus whatever is returned
// by other declared implementations of com.example.Example.
Mac
  • 14,615
  • 9
  • 62
  • 80
3

I'm using a reflection lib, which scans your classpath for all subclasses: https://github.com/ronmamo/reflections

This is how it would be done:

Reflections reflections = new Reflections("my.project");
Set<Class<? extends SomeType>> subTypes = reflections.getSubTypesOf(SomeType.class);
futchas
  • 389
  • 2
  • 10
3

It should be noted as well that this will of course only find all those subclasses that exist on your current classpath. Presumably this is OK for what you are currently looking at, and chances are you did consider this, but if you have at any point released a non-final class into the wild (for varying levels of "wild") then it is entirely feasible that someone else has written their own subclass that you will not know about.

Thus if you happened to be wanting to see all subclasses because you want to make a change and are going to see how it affects subclasses' behaviour - then bear in mind the subclasses that you can't see. Ideally all of your non-private methods, and the class itself should be well-documented; make changes according to this documentation without changing the semantics of methods/non-private fields and your changes should be backwards-compatible, for any subclass that followed your definition of the superclass at least.

Andrzej Doyle
  • 102,507
  • 33
  • 189
  • 228
3

The reason you see a difference between your implementation and Eclipse is because you scan each time, while Eclipse (and other tools) scan only once (during project load most of the times) and create an index. Next time you ask for the data it doesn't scan again, but look at the index.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
OscarRyz
  • 196,001
  • 113
  • 385
  • 569
3

You can use org.reflections library and then, create an object of Reflections class. Using this object, you can get list of all subclasses of given class. https://www.javadoc.io/doc/org.reflections/reflections/0.9.10/org/reflections/Reflections.html

    Reflections reflections = new Reflections("my.project.prefix");
    System.out.println(reflections.getSubTypesOf(A.class)));
P Mittal
  • 172
  • 6
2

Add them to a static map inside (this.getClass().getName()) the parent classes constructor (or create a default one) but this will get updated in runtime. If lazy initialization is an option you can try this approach.

Ravindranath Akila
  • 209
  • 3
  • 35
  • 45
2

I just write a simple demo to use the org.reflections.Reflections to get subclasses of abstract class:

https://github.com/xmeng1/ReflectionsDemo

Xin Meng
  • 1,982
  • 3
  • 20
  • 32
1

I needed to do this as a test case, to see if new classes had been added to the code. This is what I did

final static File rootFolder = new File(SuperClass.class.getProtectionDomain().getCodeSource().getLocation().getPath());
private static ArrayList<String> files = new ArrayList<String>();
listFilesForFolder(rootFolder); 

@Test(timeout = 1000)
public void testNumberOfSubclasses(){
    ArrayList<String> listSubclasses = new ArrayList<>(files);
    listSubclasses.removeIf(s -> !s.contains("Superclass.class"));
    for(String subclass : listSubclasses){
        System.out.println(subclass);
    }
    assertTrue("You did not create a new subclass!", listSubclasses.size() >1);     
}

public static void listFilesForFolder(final File folder) {
    for (final File fileEntry : folder.listFiles()) {
        if (fileEntry.isDirectory()) {
            listFilesForFolder(fileEntry);
        } else {
            files.add(fileEntry.getName().toString());
        }
    }
}
Cutetare
  • 913
  • 8
  • 24
1

If you intend to load all subclassess of given class which are in the same package, you can do so:

public static List<Class> loadAllSubClasses(Class pClazz) throws IOException, ClassNotFoundException {
    ClassLoader classLoader = pClazz.getClassLoader();
    assert classLoader != null;
    String packageName = pClazz.getPackage().getName();
    String dirPath = packageName.replace(".", "/");
    Enumeration<URL> srcList = classLoader.getResources(dirPath);

    List<Class> subClassList = new ArrayList<>();
    while (srcList.hasMoreElements()) {
        File dirFile = new File(srcList.nextElement().getFile());
        File[] files = dirFile.listFiles();
        if (files != null) {
            for (File file : files) {
                String subClassName = packageName + '.' + file.getName().substring(0, file.getName().length() - 6);
                if (! subClassName.equals(pClazz.getName())) {
                    subClassList.add(Class.forName(subClassName));
                }
            }
        }
    }

    return subClassList;
}
Lyfing
  • 1,778
  • 1
  • 19
  • 20
  • 1
    Just what I wanted - simplified case for limited 'search area'. I wanted to find all subclasses of an abstract base class, all in the same package. This allows 'finding' all the subclasses and managing them (adding/registering them in my case). Thanks! – Art Swri Jul 11 '22 at 14:21
0

find all classes in classpath

public static List<String> getClasses() {
        URLClassLoader urlClassLoader = (URLClassLoader) Thread.currentThread().getContextClassLoader();
        List<String> classes = new ArrayList<>();
        for (URL url : urlClassLoader.getURLs()) {
            try {
                if (url.toURI().getScheme().equals("file")) {
                    File file = new File(url.toURI());
                    if (file.exists()) {
                        try {
                            if (file.isDirectory()) {
                                for (File listFile : FileUtils.listFiles(file, new String[]{"class"}, true)) {
                                    String classFile = listFile.getAbsolutePath().replace(file.getAbsolutePath(), "").replace(".class", "");
                                    if (classFile.startsWith(File.separator)) {
                                        classFile = classFile.substring(1);
                                    }
                                    classes.add(classFile.replace(File.separator, "."));
                                }
                            } else {
                                JarFile jarFile = new JarFile(file);
                                if (url.getFile().endsWith(".jar")) {
                                    Enumeration<JarEntry> entries = jarFile.entries();
                                    while (entries.hasMoreElements()) {
                                        JarEntry jarEntry = entries.nextElement();
                                        if (jarEntry.getName().endsWith(".class")) {
                                            classes.add(jarEntry.getName().replace(".class", "").replace("/", "."));
                                        }
                                    }
                                }
                            }
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            } catch (URISyntaxException e) {
                e.printStackTrace();
            }
        }
        return classes;
    }
Enaium
  • 25
  • 1
  • 5
  • `(URLClassLoader) Thread.currentThread().getContextClassLoader();` What if the ClassLoader is not a URLClassLoader? Believe it or not, not every ClassLoader is. – VGR Aug 07 '22 at 03:52
  • @VGR System.getProperty("java.class.path") – Enaium Aug 22 '22 at 07:32
  • Applications can create ClassLoaders which load classes by means other than the java.class.path property. – VGR Aug 22 '22 at 16:20
-1

enter link description hereService Manager in java will get all implementing classes for an interface in J