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

时间:2016-12-16 17:19:02

标签: 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?

为什么baseFail行失败,而其他val不行?

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

2 个答案:

答案 0 :(得分:5)

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

EarlyDefs         ::= `{' [EarlyDef {semi EarlyDef}] `}' `with'
EarlyDef          ::=  {Annotation} {Modifier} PatVarDef

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

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

但如果你清空牙箍,你只需得到:

 val a = new Base()

哪个是非法的,new Base也是如此。

总结:

是一个允许的匿名类 :

val base1 = new Base { }

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

val baseFail = new { } with Base

允许使用适当的早期定义(非空):

val base2 = new { val n=1 } with Base

简化为new Base with Proxy,也允许:

val proxy = new { } with Base with Proxy

new { } with Base { }也会编译,但它与new Base { }完全相同,因此没有理由以这种方式编写。

答案 1 :(得分:1)

我不确定,但我能想到的最简单的解释是,空的早期初始化器只是被优化掉了,所以它相当于val baseFail = new Baseval proxy = new Base with Proxynew Base失败并显示相同的错误消息,new Base with Proxy是合法的,因为它是匿名类。如果是这样,我认为它在技术上是一个编译器错误,但相当小。