At runtime, find all classes in a Java application that extend a base class
Asked Answered
L

14

171

I want to do something like this:

List<Animal> animals = new ArrayList<Animal>();

for( Class c: list_of_all_classes_available_to_my_app() )
   if (c is Animal)
      animals.add( new c() );

So, I want to look at all of the classes in my application's universe, and when I find one that descends from Animal, I want to create a new object of that type and add it to the list. This allows me to add functionality without having to update a list of things. I can avoid the following:

List<Animal> animals = new ArrayList<Animal>();
animals.add( new Dog() );
animals.add( new Cat() );
animals.add( new Donkey() );
...

With the above approach, I can simply create a new class that extends Animal and it'll get picked up automatically.

UPDATE: 10/16/2008 9:00 a.m. Pacific Standard Time:

This question has generated a lot of great responses -- thank you. From the responses and my research, I've found that what I really want to do is just not possible under Java. There are approaches, such as ddimitrov's ServiceLoader mechanism that can work -- but they are very heavy for what I want, and I believe I simply move the problem from Java code to an external configuration file. Update 5/10/19 (11 years later!) There are now several libraries that can help with this according to @IvanNik's answer org.reflections looks good. Also ClassGraph from @Luke Hutchison's answer looks interesting. There are several more possibilities in the answers as well.

Another way to state what I want: a static function in my Animal class finds and instantiates all classes that inherit from Animal -- without any further configuration/coding. If I have to configure, I might as well just instantiate them in the Animal class anyway. I understand that because a Java program is just a loose federation of .class files that that's just the way it is.

Interestingly, it seems this is fairly trivial in C#.

Lesh answered 15/10, 2008 at 17:7 Comment(2)
After searching for awhile, it seems that this is a difficult nut to crack in Java. Here's a thread that has some info: forums.sun.com/thread.jspa?threadID=341935&start=15 The implementations in it are more than I need, I guess I'll just stick with the second implementation for now.Lesh
The link to doing this in C# doesn't show anything special. A C# Assembly is equivalent to Java JAR file. It is a collection of compiled class files (and resources). The link shows how to get the classes out of a single assembly. You can do it almost as easily with Java. The problem for you is that you need to look through all the JAR files and true loose files (directories); you would need to do the same with .NET (search a PATH of some kind or various kinds).Crumby
M
176

I use org.reflections:

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

Another example:

public static void main(String[] args) throws IllegalAccessException, InstantiationException {
    Reflections reflections = new Reflections("java.util");
    Set<Class<? extends List>> classes = reflections.getSubTypesOf(java.util.List.class);
    for (Class<? extends List> aClass : classes) {
        System.out.println(aClass.getName());
        if(aClass == ArrayList.class) {
            List list = aClass.newInstance();
            list.add("test");
            System.out.println(list.getClass().getName() + ": " + list.size());
        }
    }
}
Marinna answered 11/2, 2012 at 14:29 Comment(9)
Thanks for the link about org.reflections. I mistakenly figured this would be as easy as it is in the .Net world and this answer has saved me a lot of time.Brougham
Thank You indeed for this helpful link to the great package "org.reflections"! With that I've finally found a practicable and neat solution for my problem.Manana
Is there a way to get all reflections, i.e., without noting a specific package, but loading all available classes?Finegan
Remember that finding the classes won't solve your problem of instantiating them (line 5 animals.add( new c() ); of your code), because while Class.newInstance() exists, you have no guarantee that the specific class has a parameterless constructor (C# allows you to require that in a generic)Pru
See also ClassGraph: github.com/classgraph/classgraph (Disclaimer, I am the author). I will give a code example in a separate answer.Gamic
And how can call interface method of that class?Wozniak
@Kenji, You can call method on object or instance of particular class. See example bellow:Marinna
this was as easy as .NetMargrettmarguerie
This code seems to hard-code a package name. What happens if my code has many package names - and i don't know any of them? I need Reflection to work like it does in .NET: find everything.Treasonable
N
36

The Java way to do what you want is to use the ServiceLoader mechanism.

Also many people roll their own by having a file in a well known classpath location (i.e. /META-INF/services/myplugin.properties) and then using ClassLoader.getResources() to enumerate all files with this name from all jars. This allows each jar to export its own providers and you can instantiate them by reflection using Class.forName()

Neutron answered 15/10, 2008 at 17:7 Comment(3)
For generating the META-INF services file easily based on annotations on the classes, you can check: metainf-services.kohsuke.org or code.google.com/p/spiJankowski
Bleah. Just adds a level of complexity, but doesn't eliminate the need to both create a class and then register it in a land far far away.Hebdomad
Indeed, if you need a kind-of working solutions, Reflections / Scannotations is a good option, yet both impose an external dependency, are non-deterministic and not supported by the Java Community Process. In addition, I wanted to highlight that you can never get reliably ALL classes implementing an interface, as it is trivial to manufacture a new one out of the thin air at any time. For all its warts, Java's service loader is easy to understand and use. I would stay away from it for different reasons, but then classpath scanning is even worse: https://mcmap.net/q/144994/-java-serviceloader-with-multiple-classloadersNeutron
C
11

Think about this from an aspect-oriented point of view; what you want to do, really, is know all the classes at runtime that HAVE extended the Animal class. (I think that's a slightly more accurate description of your problem than your title; otherwise, I don't think you have a runtime question.)

So what I think you want is to create a constructor of your base class (Animal) which adds to your static array (I prefer ArrayLists, myself, but to each their own) the type of the current Class which is being instantiated.

So, roughly;

public abstract class Animal
    {
    private static ArrayList<Class> instantiatedDerivedTypes;
    public Animal() {
        Class derivedClass = this.getClass();
        if (!instantiatedDerivedClass.contains(derivedClass)) {
            instantiatedDerivedClass.Add(derivedClass);
        }
    }

Of course, you'll need a static constructor on Animal to initialize instantiatedDerivedClass... I think this'll do what you probably want. Note that this is execution-path dependent; if you have a Dog class that derives from Animal that never gets invoked, you won't have it in your Animal Class list.

Concertina answered 15/10, 2008 at 21:7 Comment(0)
G
10

The most robust mechanism for listing all subclasses of a given class is currently ClassGraph, because it handles the widest possible array of classpath specification mechanisms, including the new JPMS module system. (I am the author.)

List<Class<Animal>> animals;
try (ScanResult scanResult = new ClassGraph().whitelistPackages("com.zoo.animals")
        .enableClassInfo().scan()) {
    animals = scanResult
        .getSubclasses(Animal.class.getName())
        .loadClasses(Animal.class);
}
Gamic answered 1/8, 2018 at 3:51 Comment(3)
The result is of type List<Class<Animal>>Used
Is there a way to reclusively list subclasses? For example, I know the type of class A and I want class C. Class C extends class B which extends class A. Also, ClassGraph has always been my go-to for classpath scanners. A very fast and reliable solution. +1Chez
@CardinalSystem yes, try var subclasses = scanResult.getClassInfo("pkg.ClassName").getSubclasses(). This fetches the whole subclass graph.Gamic
C
8

Unfortunately this isn't entirely possible as the ClassLoader won't tell you what classes are available. You can, however, get fairly close doing something like this:

for (String classpathEntry : System.getProperty("java.class.path").split(System.getProperty("path.separator"))) {
    if (classpathEntry.endsWith(".jar")) {
        File jar = new File(classpathEntry);

        JarInputStream is = new JarInputStream(new FileInputStream(jar));

        JarEntry entry;
        while( (entry = is.getNextJarEntry()) != null) {
            if(entry.getName().endsWith(".class")) {
                // Class.forName(entry.getName()) and check
                //   for implementation of the interface
            }
        }
    }
}

Edit: johnstok is correct (in the comments) that this only works for standalone Java applications, and won't work under an application server.

Case answered 15/10, 2008 at 18:41 Comment(3)
This doesn't work well when classes are loaded by other means, For example, in a web or J2EE container.Cooke
You could probably do a better job, even under a container, if you queried the paths (often URL[] but not always so it may not be possible) from the ClassLoader hierarchy. Often though you must have permission since usually you would have a SecurityManager loaded within the JVM.Crumby
Worked like a charm for me! I feared this would be too heavy weight and slow, going through all classes in the classpath, but actually I limited the files I checked to those containing "-ejb-" or other recognizable file names, and it's still 100 times faster than starting up an embedded glassfish or EJBContainer. In my particular situation, I then analysed the classes looking for "Stateless", "Stateful", and "Singleton" annotations, and if I saw them, I added the JNDI Mapped name to my MockInitialContextFactory. Thanks again!Dessalines
S
4

You could use ResolverUtil (raw source) from the Stripes Framework
if you need something simple and quick without refactoring any existing code.

Here's a simple example not having loaded any of the classes:

package test;

import java.util.Set;
import net.sourceforge.stripes.util.ResolverUtil;

public class BaseClassTest {
    public static void main(String[] args) throws Exception {
        ResolverUtil<Animal> resolver = new ResolverUtil<Animal>();
        resolver.findImplementations(Animal.class, "test");
        Set<Class<? extends Animal>> classes = resolver.getClasses();

        for (Class<? extends Animal> clazz : classes) {
            System.out.println(clazz);
        }
    }
}

class Animal {}
class Dog extends Animal {}
class Cat extends Animal {}
class Donkey extends Animal {}

This also works in an application server as well since that's where it was designed to work ;)

The code basically does the following:

  • iterate over all the resources in the package(s) you specify
  • keep only the resources ending in .class
  • Load those classes using ClassLoader#loadClass(String fullyQualifiedName)
  • Checks if Animal.class.isAssignableFrom(loadedClass);
Skeg answered 21/11, 2013 at 12:7 Comment(0)
F
2

Java dynamically loads classes, so your universe of classes would be only those that have already been loaded (and not yet unloaded). Perhaps you can do something with a custom class loader that could check the supertypes of each loaded class. I don't think there's an API to query the set of loaded classes.

Furbish answered 15/10, 2008 at 17:55 Comment(0)
S
2

use this

public static Set<Class> getExtendedClasses(Class superClass)
{
    try
    {
        ResolverUtil resolver = new ResolverUtil();
        resolver.findImplementations(superClass, superClass.getPackage().getName());
        return resolver.getClasses();  
    }
    catch(Exception e)
    {Log.d("Log:", " Err: getExtendedClasses() ");}

    return null;
}

getExtendedClasses(Animals.class);

Edit:

  • library for (ResolverUtil) : Stripes
Solleret answered 7/4, 2018 at 19:44 Comment(1)
You'll need to say a bit more about ResolverUtil. What is it? What library does it come from?Lesh
L
1

Thanks all who answered this question.

It seems this is indeed a tough nut to crack. I ended up giving up and creating a static array and getter in my baseclass.

public abstract class Animal{
    private static Animal[] animals= null;
    public static Animal[] getAnimals(){
        if (animals==null){
            animals = new Animal[]{
                new Dog(),
                new Cat(),
                new Lion()
            };
        }
        return animals;
    }
}

It seems that Java just isn't set up for self-discoverability the way C# is. I suppose the problem is that since a Java app is just a collection of .class files out in a directory / jar file somewhere, the runtime doesn't know about a class until it's referenced. At that time the loader loads it -- what I'm trying to do is discover it before I reference it which is not possible without going out to the file system and looking.

I always like code that can discover itself instead of me having to tell it about itself, but alas this works too.

Thanks again!

Lesh answered 15/10, 2008 at 19:4 Comment(1)
Java is set up for self-discovery. You just need to do a bit more work. See my answer for details.Lyre
H
1

Using OpenPojo you can do the following:

String package = "com.mycompany";
List<Animal> animals = new ArrayList<Animal>();

for(PojoClass pojoClass : PojoClassFactory.enumerateClassesByExtendingType(package, Animal.class, null) {
  animals.add((Animal) InstanceFactory.getInstance(pojoClass));
}
Hexane answered 10/2, 2015 at 5:23 Comment(0)
P
0

This is a tough problem and you will need to find out this information using static analysis, its not available easily at runtime. Basically get the classpath of your app and scan through the available classes and read the bytecode information of a class which class it inherits from. Note that a class Dog may not directly inherit from Animal but might inherit from Pet which is turn inherits from Animal,so you will need to keep track of that hierarchy.

Prickett answered 15/10, 2008 at 18:52 Comment(0)
B
0

One way is to make the classes use a static initializers... I don't think these are inherited (it won't work if they are):

public class Dog extends Animal{

static
{
   Animal a = new Dog();
   //add a to the List
}

It requires you to add this code to all of the classes involved. But it avoids having a big ugly loop somewhere, testing every class searching for children of Animal.

Blakley answered 15/10, 2008 at 19:29 Comment(1)
This doesn't work in my case. Since the class is not referenced anywhere it is never loaded so the static initializer is never called. It seems that a static initializer is called before everything else when the class is loaded -- but in my case the class is never loaded.Lesh
H
0

I solved this problem pretty elegantly using Package Level Annotations and then making that annotation have as an argument a list of classes.

Find Java classes implementing an interface

Implementations just have to create a package-info.java and put the magic annotation in with the list of classes they want to support.

Hewart answered 29/4, 2011 at 12:15 Comment(0)
T
0

Since directly using newInstance() is deprecated, you can do it this way using Reflections.

Reflections r = new Reflections("com.example.location.of.sub.classes")
Set<Class<? extends Animal>> classes = r.getSubTypesOf(Animal.class);

classes.forEach(c -> {
    Animal a = c.getDeclaredConstructor().newInstance();
    //a is your instance of Animal.
});
Tusker answered 3/5, 2021 at 18:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.