2011-12-05 18 views
4

我有大量简单的命令行Scala应用程序,这些应用程序共享相当多的常见结构。他们都从scala.App继承,这很好。我想将这些命令行应用程序的共享结构重构为一个共同的特征,然后我可以继承我的(更简单的)命令行应用程序类。问题在于一些常见的结构包括解析命令行参数。在Scala特征中获取命令行参数

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 
} 

这将编译,但无法与NPE,当涉及到实际取消引用args,大概是因为App特质尚未初始化时间。像使用DelayedInit一样进行实验,更改特征顺序并将BaseScript中的App继承更改为自我类型声明什么也不做。在BaseScript中将组件声明为“懒惰”是可行的,但我也希望在初始化期间实际使用这些组件(例如,根据配置设置日志目录并加载JDBC驱动程序类),因此懒惰的好处将会丢失。有什么我可以做的,让命令行参数在BaseScript特征中可见和初始化?

+0

没有多少我可以添加除了检查scopts:https://github.com/jstrachan/scopt – wheaties

回答

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 
    } 
} 
+0

可悲的是,这仍然没有' t在BaseScript初始化后得到App初始化,因此对“args”的调用仍然是NPE。污物。 –

+0

我提供了一些似乎适用于我自己的代码。如果没有看到您的所有案例,我可能会漏掉一些东西,但是无论何时扩展BaseScript,删除“with App”组合都可能解决问题。 –

+0

这样做。在MyScript上使用“与应用程序”是搞砸了。谢谢。 –

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 
} 
+0

编辑最初的问题,解释为什么那个可悲的不会为我工作 –

1

望着Appsource似乎可以覆盖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

+0

我试过这个,并得到了弃用警告: “重写方法主要在特性应用程序已弃用:主要不应该被重写” –

+1

已经5年了,我敢肯定我的答案已经过时。 – huynhjl

+1

我发现你可以在你的代码中使用args,而不需要扩展App类中的任何主函数。没有在代码中明确定义的参数。我刚刚使用val numSegments = args(0)。toInt在我的情况,并使用从命令行传递的值。 –