使用早期初始化程序语法时似乎有一种微妙。
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
,只是混合它。
答案 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 Base
和val proxy = new Base with Proxy
。 new Base
失败并显示相同的错误消息,new Base with Proxy
是合法的,因为它是匿名类。如果是这样,我认为它在技术上是一个编译器错误,但相当小。