获取Scala特征中的命令行参数

时间:2011-12-05 20:28:56

标签: scala

我有大量简单的命令行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特性中可见和初始化?

3 个答案:

答案 0 :(得分:4)

我认为你最好的选择是将你的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
  }
}

答案 1 :(得分:3)

您是否尝试过使用lazy val(而不是扩展App特征)?

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

答案 2 :(得分:1)

查看App source,您似乎可以在应用代码运行之前覆盖main以使用args执行操作:

trait AppUtil extends App {
  def myInit(args: Array[String]) {
    println("args " + args.size)
  }
  override def main(args: Array[String]) {
    myInit(args)
    super.main(args)
  }
}

我怀疑App来源可以为您提供重写自定义App的灵感。代码真的不长,您可以更好地控制事物,例如您使用args执行的操作,运行前后的事件main