想象一下,我有一个带有单个抽象方法的Tokenizer类.
trait Tokenizer {
def tokenize(sentence: String): List[String]
}
Run Code Online (Sandbox Code Playgroud)
我将实现一些提供实现的tokenizer.我希望每个Tokenizer都有一个main方法.我的第一个想法是编写这样的代码:
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) {
}
Run Code Online (Sandbox Code Playgroud)
但是,我得到错误"超级构造函数不能传递自引用,除非参数是通过名称声明的".我可以重命名object TokenizerOne
,object TokenizerOneMain
但我想保持它与class
.有一个更好的方法吗?
更新:此问题似乎是由model
TokenizerOne 的隐式构造函数参数引起的.
这是一个减少代码示例,给出相同的错误,
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
Run Code Online (Sandbox Code Playgroud)
字节码有助于解释发生了什么.来自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);
...
Run Code Online (Sandbox Code Playgroud)
我们看到该类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
Run Code Online (Sandbox Code Playgroud)
啊,这是在伴侣对象中定义的,它属于类Bar$
(只有Scala编译器应该知道它).
所以似乎正在发生的事情是,extends Foo(new Bar())
你Bar
在初始化Bar
超类(在Bar
实际构造对象之前)尝试访问对象中的方法.
如果这不是Scala编译器中的错误,那么这是一个令人困惑的错误消息!我不能说哪个.我在错误跟踪器中提交了问题SI-5000.
作为解决方法,您可以避免使用默认值:object Bar extends Foo(new Bar("bar"))
.