我希望在实例化类之前在scala伴随对象的主体中运行一些代码。我们的想法是在Set中注册一堆对象。这是代码
trait Delegate {
def make: Ins
}
//EDIT: Changed constructor to private
//class Ins
class Ins private()
//this is the companion object that will be registered with the InsDelegate
object Ins extends Delegate{
//here is the code that do the registration but doesn't run
InsDelegate.register(this)
override def make: Ins = {
println("This is an Ins")
new Ins()
}
}
以下是InsDelegate的代码
object InsDelegate {
private val objectSet = new mutable.HashSet[Delegate]()
def register(obj: Delegate): Unit = objectSet.add(obj)
def getRegisteredObj: Set[Delegate] = objectSet.toSet
}
当我运行此测试时,没有任何内容被打印出来。
object test extends App {
InsDelegate.getRegisteredObj.foreach(_.make)
}
注册伴随对象的代码不会运行。我知道,与java不同,为了运行伴随对象代码,您需要实例化对象的类。我如何完成我想要做的事情?
答案 0 :(得分:3)
Scala 对象是惰性的,因此它们仅在首次使用时构建。在您的示例中,test
应用程序从不创建任何实例,因此永远不会构造对象Ins
。
您的代码应该可以运行,但您需要在测试代码中创建类Ins
的实例:
object test extends App {
val temp = Ins.make()
InsDelegate.getRegisteredObj.foreach(_.make)
}
顺便提一下,副作用函数(Delegate.make
)的约定是括号;没有括号的版本表示该函数没有副作用,make
明显具有副作用(注册Ins
对象,创建新的Ins
元素。)
另一个 Scala 约定是命名工厂方法apply
,而不是make
。如果您这样做,则可以使用Ins
而不是Ins()
创建新的Ins.make()
类实例。 (Ins()
被解释为与Ins.apply()
相同。)
更新:忘记提及:如果您想先注册Ins
而不先创建任何实例,则需要以某种方式引用它。这很快就会产生丑陋的解决方案:
object Ins extends Delegate{
InsDelegate.register(this)
// Dummy method to get object to register itself.
def register(): Unit = {}
override def make: Ins = {
println("This is an Ins")
new Ins()
}
}
object InsDelegate {
private val objectSet = new mutable.HashSet[Delegate]()
def register(obj: Delegate): Unit = objectSet.add(obj)
def getRegisteredObj: Set[Delegate] = objectSet.toSet
// Create delegate objects...
Ins.register()
}
但是,如果我们要解决这个问题,我们不妨放弃注册并在InsDelegate
对象中添加对象:
object Ins extends Delegate{
override def make: Ins = {
println("This is an Ins")
new Ins()
}
}
object InsDelegate {
// Set of delegate objects available. Note: this is public, replaces getRegisteredObj.
val objectSet: Set[Delegate] = Set(Ins)
}
缺点是Delegate
个对象不再自行注册,但这是伪装的祝福,因为您现在可以单独测试代理创建InsDelegate
。
答案 1 :(得分:0)
我知道,与java不同,为了运行伴随对象代码,您需要实例化对象的类
实际上,您的Java代码会有相同的结果。在这两种情况下你需要做的是加载类,并实例化它只是一种方法。您还可以使用Class.forName
,ClassLoader.loadClass
,加载在签名中某处使用它的任何类...一个非常着名的案例是(或者,在JDBC 4.0之前)loading JDBC drivers。
不幸的是,在Scala中,您需要加载的类实际上是Ins$
(伴随对象的类)并实例化Ins
(或以其他方式加载)并不一定足够。