At runtime, find all classes in a Java application that extend a base class
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());
}
}
}
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()
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.
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.