SomeTrait的早期初始化程序`new {}失败

Kel*_*vin 3 scala traits anonymous-class

使用早期初始化器语法时似乎有一种微妙之处.

trait Base { def callMe = "callMe" }
trait Proxy { this: Base => def call = s"proxied: $callMe" }

val base1 = new Base { }   // non-early init works
val baseFail = new { } with Base   // error: trait Base is abstract; cannot be instantiated
val base2 = new { val n=1 } with Base   // why does this fix the failure?
val proxy = new { } with Base with Proxy   // why doesn't this fail?
Run Code Online (Sandbox Code Playgroud)

为什么baseFail线路出现故障,而另一条线路val没有?

错误消息也令人困惑 - 我不是试图实例化Base,只是混合它.

Mic*_*jac 5

当你写作时new { } with Base,技术上没有任何早期定义.根据SLS 5.1.6,编译器会查找如下模式:

EarlyDefs         ::= `{' [EarlyDef {semi EarlyDef}] `}' `with'
EarlyDef          ::=  {Annotation} {Modifier} PatVarDef
Run Code Online (Sandbox Code Playgroud)

虽然它没有明确说明当定义序列为空时会发生什么,但它似乎只是删除它们,因为在编译时val a = new { val x = 1 } with Base,你会在解析阶段之后得到类似的东西:

val a = {
  final class $anon extends Base {
    val x = _;
    def <init>() = {
      val x = 1;
      super.<init>();
      ()
    }
  };
  new $anon()
}
Run Code Online (Sandbox Code Playgroud)

但如果你清空括号,你只需得到:

 val a = new Base()
Run Code Online (Sandbox Code Playgroud)

这是违法的new Base.


总结一下:

是匿名类,允许:

val base1 = new Base { }
Run Code Online (Sandbox Code Playgroud)

简化为new Base,这是非法的:

val baseFail = new { } with Base
Run Code Online (Sandbox Code Playgroud)

是否允许适当的早期定义(非空):

val base2 = new { val n=1 } with Base
Run Code Online (Sandbox Code Playgroud)

简化new Base with Proxy也允许:

val proxy = new { } with Base with Proxy
Run Code Online (Sandbox Code Playgroud)

new { } with Base { }也编译,但它完全相同new Base { },所以没有理由这样写.