如果我知道对象的类名,如何获取它并调用其方法?

时间:2011-03-07 12:23:05

标签: scala object

我有一个名为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方法?

2 个答案:

答案 0 :(得分:6)

在Scala中执行此操作通常没什么意义。只需将一些代码放在任何object的正文中,它就会在首次初始化该对象时执行,从而避免在预先初始化所有内容时出现令人讨厌的性能损失。

通常,查找特定类型的所有子类需要完整的类路径扫描。有一些库可以做到这一点,但其中一个更常见的是Apache's commons-discover

然而......这是动态代码,它使用反射,而且它真的不是惯用的。 Scala拥有比这更尖锐的工具,所以请不要尝试用这种暴力来摆动钝器!

答案 1 :(得分:0)

我不完全同意凯文。有一些例外。例如,我写了一个Scala桌面应用程序。我将核心和模块分成两部分。在启动时,核心将所有模块加载到GUI中。那时核心只是得到了模块的名称,它不需要初始化一些东西。然后我将所有模块的init代码放在init()函数中。当用户执行模块时,将调用该函数。

@Freewind:关于Scala中的反射,它在Java中完全相同。请注意,Java中用于反射的方法用于Java对象 - 而不是Scala。对不起我的英语。我的意思是这些方法不适用于Scala objecttrait

例如:

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]))

因为Inittrait,而Java方法isAssignableFrom(Class)不知道trait

我不确定我的问题是否对您有用,但here it is