1

I have a trait, named Init:

package test
trait Init {
    def init(): Any
}

There are some classes and an object, extends this trait:

package test
object Config extends Init {
    def init() = { loadFromFile(...) }
}
class InitDb extends Init {
    def init() = { initdb() }
}

When app has started, I will find all classes and objects which extends Init, and invoke their init method.

package test
object App {
    def main(args: Array[String]) {
        val classNames: List[String] = findAllNamesOfSubclassOf[Init]
        println(classNames) // -> List(test.Config$, test.InitDb)
        classNames foreach { name =>
             Class.forName(name).newInstance().asInstanceOf[Init].init() // ***
        }
    }
}

Please note the "*" line. For test.InitDb, it's OK. But for test.Config$, when newInstance(), it throws an exception said we can't access its private method.

My problem is, how to get that object, and run its init method?

Freewind
  • 193,756
  • 157
  • 432
  • 708

2 Answers2

6

There's usually little point to doing this in Scala. Just put some code in the body of any object and it'll be executed when that object is first initialised, saving you the nasty performance hit of pre-initialising everything.

In general though, finding all subclasses of a particular type requires a full classpath scan. There are a few libraries to do this, but one of the more common is Apache's commons-discover

However... This is dynamic code, it uses reflection, and it's really NOT idiomatic. Scala has sharper tools than that, so please don't try and swing the blunt ones with such violence!

Kevin Wright
  • 49,540
  • 9
  • 105
  • 155
  • thank you. I was trying to put some startup code in some objects in a webapp, but I found they haven't be executed when jetty was starting, since no other class requires them. But I need them executed when jetty started, no matter if there are other classes access them. How to do so? My solution is let them extend `Init`, and find and invoke them as my example. Could you give me more advices? – Freewind Mar 07 '11 at 12:48
  • Put all the code in a dedicated startup servlet, or use whatever equivalent mechanism your web framework provides. If these singletons aren't actually used anywhere else, then you really want to question why you're defining them in the first place... – Kevin Wright Mar 07 '11 at 12:58
0

I don't totally agree with Kevin. There are some exceptions. For example I wrote a Scala desktop app. I split the core and the modules into two parts. At startup time the core loads all modules into GUI. At that time the core just gets the name of modules, it doesn't need to initialize something. Then I put all module's init code in an init() function. That function will be called when user executes the module.

@Freewind: About reflection in Scala, it's absolutely the same in Java. Just please note that the methods from Java which are used with reflection, are used for Java objects - not Scala. I'm sorry for my English. I mean those methods can not work with Scala object, trait.

For example:

var classLoader = new java.net.URLClassLoader(
    Array(new File("module.jar").toURI.toURL),
    /*
     * need to specify parent, so we have all class instances
     * in current context
     */
    this.getClass.getClassLoader)

var clazz = classLoader.loadClass("test.InitDb")
if (classOf[Init].isAssignableFrom(clazz))
    var an_init = clazz.newInstance.asInstanceOf[Init];

But you can not do it the opposite way:

if (clazz.isAssignableFrom(classOf[Init]))

Because Init is a trait, and Java method isAssignableFrom(Class) doesn't know trait.

I'm not sure if my question is useful for you, but here it is.

Community
  • 1
  • 1