How to find all classes in a package using reflection in kotlin

Here's an example of querying classloader resources, adapted from https://www.javaworld.com/article/2077477/java-tip-113--identify-subclasses-at-runtime.html

Requires Java 8 or higher.

// Call this function using something like:
//     findClasses("com.mypackage.mysubpackage")
// Modified from https://www.javaworld.com/article/2077477/java-tip-113--identify-subclasses-at-runtime.html
fun findClasses(pckgname: String) {
    // Translate the package name into an absolute path
    var name = pckgname
    if (!name.startsWith("/")) {
        name = "/$name"
    }
    name = name.replace('.', '/')

    // Get a File object for the package
    val url: URL = Launcher::class.java.getResource(name)
    val directory = File(url.getFile())

    println("Finding classes:")
    if (directory.exists()) {
        // Get the list of the files contained in the package
        directory.walk()
            .filter { f -> f.isFile() && f.name.contains('$') == false && f.name.endsWith(".class") }
            .forEach {
                val fullyQualifiedClassName = pckgname +
                    it.canonicalPath.removePrefix(directory.canonicalPath)
                    .dropLast(6) // remove .class
                    .replace('/', '.')
                try {
                    // Try to create an instance of the object
                    val o = Class.forName(fullyQualifiedClassName).getDeclaredConstructor().newInstance()
                    if (o is MyInterfaceOrClass) {
                        println(fullyQualifiedClassName)
                        // Optionally, make a function call here: o.myFunction()
                    }
                } catch (cnfex: ClassNotFoundException) {
                    System.err.println(cnfex)
                } catch (iex: InstantiationException) {
                    // We try to instantiate an interface
                    // or an object that does not have a
                    // default constructor
                } catch (iaex: IllegalAccessException) {
                    // The class is not public
                }
            }
    }
}

Kotlin on the JVM suffers the same issue as Java in this regard due to the implementation of class loaders.

Class loaders are not required to tell the VM which classes it can provide, instead they are just handed requests for classes, and have to return a class or throw an exception.

Source and more information: Can you find all classes in a package using reflection?

To summarize the linked thread, there are a number of solutions that allow you to inspect your current class path.

  • The Reflections library is pretty straight forward and has a lot of additional functionality like getting all subtypes of a class, get all types/members annotated with some annotation, optionally with annotation parameters matching, etc.
  • Guava has ClassPath, which returns ClassInfo POJO's - not enough for your use case, but useful to know as Guava is available almost everywhere.
  • Write your own by querying classloader resources and code sources. Would not suggest this route unless you absolutely cannot add library dependencies.

Tags:

Kotlin