0

I have a project in Kotlin with two modules (ProjectName.ModuleA and ProjectName.ModuleB). Within these modules I have a package (com.company.projectName.moduleA and com.company.projectName.moduleB). Module B references Module A.

In ModuleA I have a library that defines an interface (called Flag). This interfaces has implementations in ModuleA and ModuleB. I'm now writing tests for ModuleB.

The thing is that I'd like a function in ModuleA (the main library) that loads all the classes that implement the Flag interface. What I'm expecting is that, when I run the tests of ModuleA only the ModuleA implementations are loaded, but when I run the tests of ModuleB, both ModuleA and ModuleB implementations are loaded, because when I use this library in the "real world" I will like to load all the implementations that exist in the libraries referenced.

I have this code, but this code only loads the classes of the current package.

private fun findFlags(packageName: String) {
    // Translate the package name into an absolute path
    var name = packageName
    if (!name.startsWith("/")) {
        name = "/$name"
    }
    name = name.replace('.', '/')
    // Get a File object for the package
    val url: URL = SimplyTheFlag::class.java.getResource(name)
    val directory = File(url.file)

    if (directory.exists()) {
        // Get the list of the files contained in the package
        directory.walk()
            .filter { f -> f.isFile && !f.name.contains('$') && f.name.endsWith(".class") }
            .forEach { it ->
                val className = it.canonicalPath.removePrefix(directory.canonicalPath)
                    .dropLast(6) // remove .class
                    .drop(1) // drop initial .
                    .replace('/', '.')
                val fullyQualifiedClassName = "$packageName.$className"

                val isFlag = Class.forName(fullyQualifiedClassName).interfaces.any { i -> i.simpleName == Flag::class.java.simpleName }
                if (isFlag) {
                    availableFlags[className] = fullyQualifiedClassName
                }
            }
    }
}

How can I implement this?

vgaltes
  • 1,150
  • 11
  • 18
  • 3
    There is no simple way to list classes in Java or Kotlin. Your current implementation works for you locally during the development, but if you build the application, it will stop working. You can't assume there is a directory with `*.class` files. It may be a jar file instead of a directory. It may use entirely different format e.g. `*.dex` files in Android. Proper way of doing this kind of stuff in JVM is by using the [ServiceLoader API](https://docs.oracle.com/javase/8/docs/api/java/util/ServiceLoader.html). – broot Jul 02 '23 at 17:59
  • Thanks! For now I'm trying this solution (https://stackoverflow.com/questions/520328/can-you-find-all-classes-in-a-package-using-reflection/22462785#22462785) that seems to work locally. I'll try with the application built. – vgaltes Jul 03 '23 at 07:38

1 Answers1

0

You can try the reflection api mentioned in this questions second answer: Java - Get a list of all Classes loaded in the JVM

However I have not tested this with Kotlin. Here is a direct link to the repository: https://github.com/ronmamo/reflections

The api enables you to load all subtypes of any given type (including interfaces of course). If it works with Kotlin, it might solve your problem.

TreffnonX
  • 2,924
  • 15
  • 23
  • Looks like Reflections "is currently NOT under active development or maintenance", but I'll give it a try. – vgaltes Jul 03 '23 at 07:34