我有一个名为Init
的特性:
package test
trait Init {
def init(): Any
}
有一些类和一个对象,扩展了这个特性:
package test
object Config extends Init {
def init() = { loadFromFile(...) }
}
class InitDb extends Init {
def init() = { initdb() }
}
当应用程序启动时,我会找到所有扩展Init
的类和对象,并调用他们的init
方法。
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() // ***
}
}
}
请注意“ * ”行。对于test.InitDb
,没关系。但是对于test.Config$
,当newInstance()
时,它会抛出异常,表示我们无法访问其私有方法。
我的问题是,如何获取该对象,并运行其init
方法?
答案 0 :(得分:6)
在Scala中执行此操作通常没什么意义。只需将一些代码放在任何object
的正文中,它就会在首次初始化该对象时执行,从而避免在预先初始化所有内容时出现令人讨厌的性能损失。
通常,查找特定类型的所有子类需要完整的类路径扫描。有一些库可以做到这一点,但其中一个更常见的是Apache's commons-discover
然而......这是动态代码,它使用反射,而且它真的不是惯用的。 Scala拥有比这更尖锐的工具,所以请不要尝试用这种暴力来摆动钝器!
答案 1 :(得分:0)
我不完全同意凯文。有一些例外。例如,我写了一个Scala桌面应用程序。我将核心和模块分成两部分。在启动时,核心将所有模块加载到GUI中。那时核心只是得到了模块的名称,它不需要初始化一些东西。然后我将所有模块的init代码放在init()
函数中。当用户执行模块时,将调用该函数。
@Freewind:关于Scala中的反射,它在Java中完全相同。请注意,Java中用于反射的方法用于Java对象 - 而不是Scala。对不起我的英语。我的意思是这些方法不适用于Scala object
,trait
。
例如:
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];
但你不能以相反的方式做到这一点:
if (clazz.isAssignableFrom(classOf[Init]))
因为Init
是trait
,而Java方法isAssignableFrom(Class)
不知道trait
。
我不确定我的问题是否对您有用,但here it is。