2011-09-16 108 views
2

想象一下,我有一个带有单个抽象方法的Tokenizer类。扩展一个类,每个类都有很多实现,每个实现都有Scala中的主要方法

trait Tokenizer { 
    def tokenize(sentence: String): List[String] 
} 

我将实现一些提供实现的标记器。我希望每个Tokenizer都有一个主要的方法。我首先想到的是写这样的代码:

abstract class TokenizerMain(tokenizer: Tokenizer) { 
    def main(args: Array[String]) = println(tokenizer.tokenize(args(0)).mkString(" ")) 
} 

class TokenizerOne(val model: String = "foo") extends Tokenizer { 
    def tokenize(sentence: String) = List("asdf") 
} 

object TokenizerOne extends TokenizerMain(new TokenizerOne) { 
} 

但是,我得到的错误“超级构造函数不能被传递一个自我引用,除非参数是通过名称声明”。我可以将object TokenizerOne重命名为object TokenizerOneMain,但我想保留它与class相同。有一个更好的方法吗?

更新:此问题似乎是由TokenizerOne的隐式构造函数参数model造成的。

回答

7

这里有一个减少代码示例,给出了同样的错误,

class Foo(t: Any) 
class Bar(x: String = "bar") 
object Bar extends Foo(new Bar()) 
    //     ^
    // Error, super constructor cannot be passed a self reference unless 
    // parameter is declared by-name 

字节码可以解释发生了什么事情。从REPL,

scala> class Foo(t: Any) 

scala> class Bar(x: String = "bar") 

scala> :javap -v Bar 

Compiled from "<console>" 
public class Bar extends java.lang.Object implements scala.ScalaObject 
... 
{ 
public Bar(java.lang.String); 
... 

我们看到类Bar只有一个构造函数,它需要一个String参数。但我们知道Bar也有一个构造函数,它使用默认值x = "bar",这是从哪里来的?

scala> :javap -v Bar$ 
... 
public java.lang.String init$default$1(); 
    Code: 
    Stack=1, Locals=1, Args_size=1 
    0: ldC#16; //String bar 
    2: areturn 
    LineNumberTable: 
    line 7: 0 

啊,在同伴的对象,属于Bar$类(仅限Scala编译器应该知道的事情)的已定义。

那么,什么似乎是发生的是,在extends Foo(new Bar())你想Bar的超一流的初始化过程中访问对象Bar的方法(这是对象Bar之前实际上是构造)。

如果这不是Scala编译器中的错误,那么这是一个令人困惑的错误消息!我不能说哪个。我在错误跟踪器中提交了问题SI-5000

作为解决方法,您可以避免默认值:object Bar extends Foo(new Bar("bar"))

+0

我正在使用2.9.1。我忘记了TokenizerOne类的隐式构造函数参数 - 我的歉意。随着这个构造函数参数的增加,问题就会发生。 – schmmd

相关问题