Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

kotlin

Is it possible to find all kotlin classes in a given package?

I also need only annotated classes but it's not a big deal. Any suggestions ?

like image 205
nrudnyk Avatar asked Feb 22 '17 20:02

nrudnyk


2 Answers

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.
like image 131
F. George Avatar answered Nov 18 '22 17:11

F. George


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
                }
            }
    }
}
like image 29
Francis Huang Avatar answered Nov 18 '22 19:11

Francis Huang