I have been searching for a while and there seems to be different approaches, here is a summary:
-
reflections library is pretty popular if u don’t mind adding the dependency. It would look like this:
Reflections reflections = new Reflections("firstdeveloper.examples.reflections"); Set<Class<? extends Pet>> classes = reflections.getSubTypesOf(Pet.class); -
ServiceLoader (as per erickson answer) and it would look like this:
ServiceLoader<Pet> loader = ServiceLoader.load(Pet.class); for (Pet implClass : loader) { System.out.println(implClass.getClass().getSimpleName()); // prints Dog, Cat }Note that for this to work you need to define
Petas a ServiceProviderInterface (SPI) and declare its implementations. you do that by creating a file inresources/META-INF/serviceswith the nameexamples.reflections.Petand declare all implementations ofPetin itexamples.reflections.Dog examples.reflections.Cat -
package-level annotation. here is an example:
Package[] packages = Package.getPackages(); for (Package p : packages) { MyPackageAnnotation annotation = p.getAnnotation(MyPackageAnnotation.class); if (annotation != null) { Class<?>[] implementations = annotation.implementationsOfPet(); for (Class<?> impl : implementations) { System.out.println(impl.getSimpleName()); } } }and the annotation definition:
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.PACKAGE) public @interface MyPackageAnnotation { Class<?>[] implementationsOfPet() default {}; }and you must declare the package-level annotation in a file named
package-info.javainside that package. here are sample contents:@MyPackageAnnotation(implementationsOfPet = {Dog.class, Cat.class}) package examples.reflections;Note that only packages that are known to the ClassLoader at that time will be loaded by a call to
Package.getPackages().
In addition, there are other approaches based on URLClassLoader that will always be limited to classes that have been already loaded, Unless you do a directory-based search.