运行"静态呼叫"在类实例化Scala之前的对象中

时间:2017-05-01 14:15:26

标签: scala

我希望在实例化类之前在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不同,为了运行伴随对象代码,您需要实例化对象的类。我如何完成我想要做的事情?

2 个答案:

答案 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.forNameClassLoader.loadClass,加载在签名中某处使用它的任何类...一个非常着名的案例是(或者,在JDBC 4.0之前)loading JDBC drivers

不幸的是,在Scala中,您需要加载的类实际上是Ins$(伴随对象的类)并实例化Ins(或以其他方式加载)并不一定足够。