带有 SomeTrait 的早期初始化器 `new {} 失败
Early initializer `new {} with SomeTrait` fails
使用早期初始化语法时似乎有些微妙之处。
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
,只是将其混入。
我不确定,但对于这种行为我能想到的最简单的解释是空的早期初始化器被简单地优化掉了,所以它等同于 val baseFail = new Base
和 val proxy = new Base with Proxy
。 new Base
失败并显示相同的错误消息,而 new Base with Proxy
是合法的,因为它是匿名的 class。如果是这样,我认为它在技术上是一个编译器错误,但相当小。
当您编写 new { } with Base
时,技术上 没有 任何早期定义。根据 SLS 5.1.6,编译器查找这样的模式:
EarlyDefs ::= `{' [EarlyDef {semi EarlyDef}] `}' `with'
EarlyDef ::= {Annotation} {Modifier} PatVarDef
虽然它没有明确说明当定义序列为空时会发生什么,但它似乎只是将它们删除,因为当你编译 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()
}
但是如果你清空大括号,你只会得到:
val a = new Base()
这是非法的,new Base
。
总结:
是匿名的class,这是允许的
:
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 { }
完全一样,所以没理由这么写。
使用早期初始化语法时似乎有些微妙之处。
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
,只是将其混入。
我不确定,但对于这种行为我能想到的最简单的解释是空的早期初始化器被简单地优化掉了,所以它等同于 val baseFail = new Base
和 val proxy = new Base with Proxy
。 new Base
失败并显示相同的错误消息,而 new Base with Proxy
是合法的,因为它是匿名的 class。如果是这样,我认为它在技术上是一个编译器错误,但相当小。
当您编写 new { } with Base
时,技术上 没有 任何早期定义。根据 SLS 5.1.6,编译器查找这样的模式:
EarlyDefs ::= `{' [EarlyDef {semi EarlyDef}] `}' `with'
EarlyDef ::= {Annotation} {Modifier} PatVarDef
虽然它没有明确说明当定义序列为空时会发生什么,但它似乎只是将它们删除,因为当你编译 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()
}
但是如果你清空大括号,你只会得到:
val a = new Base()
这是非法的,new Base
。
总结:
是匿名的class,这是允许的 :
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 { }
完全一样,所以没理由这么写。