我有大量的简单Scala命令行应用程序,它们共享很多通用结构。它们全部都继承自scala.App,这很好。我想将这些命令行应用程序的共享结构重构为一个共同的特征,然后可以将其继承到我的(更简单的)命令行应用程序类中。出现问题的原因在于某些通用结构包括命令行参数的解析。

object MyScript extends BaseScript with App{
   //small bits of business logic using components defined in BaseScript
}

trait BaseScript extends App{
    val configuration = loadConfiguration(args(0))
    //setup a bezillion components, usable from any of the scripts, based on the configuration
}


它可以编译,但是在实际取消对args的引用时会出现NPE失败,这可能是因为App特性尚未初始化。更改特征顺序和将BaseScript中App的继承更改为自类型声明,就像使用DelayedInit进行实验一样。在BaseScript中将组件声明为“惰性”是可行的,但我也希望在初始化期间实际使用这些组件(例如,根据配置来设置日志目录和加载JDBC驱动程序类),因此失去了懒惰的好处。我可以做些什么来使命令行参数在BaseScript特性中可见并初始化吗?

最佳答案

我认为您最好的选择是将您的BaseScript特性更改为类,这有两个原因。首先是与类相比,特征初始化以相反的顺序执行。请参见this question on initialization behavior。其次,从语义上讲,BaseScript更多的是超类,而不是其他的行为。我认为您会发现这可以简化事情。

当执行MyScript时,以下代码首先初始化BaseScript类。 BaseScript依次取决于App特性,并强制其首先进行初始化。

object MyScript extends BaseScript {
  //small bits of business logic using components defined in BaseScript
  println( "running" )
  println( "arg(0): " + configuration )
}

class BaseScript extends App {
  val configuration = loadConfiguration(args)
  //setup a bezillion components, usable from any of the scripts, based on the configuration
  def loadConfiguration( args: Array[String] ) = {
    println( "configuring" )
    if ( args.length > 0 ) args(0) else null
  }
}

10-04 14:24