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
Pet
as a ServiceProviderInterface (SPI) and declare its implementations. you do that by creating a file inresources/META-INF/services
with the nameexamples.reflections.Pet
and declare all implementations ofPet
in 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.java
inside 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.