Ранний инициализатор `new {} с SomeTrait` завершается неудачно
Кажется, есть тонкость при использовании раннего синтаксиса инициализатора.
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 ответа
Когда ты пишешь 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
,
Подвести итоги:
Это анонимный класс, который допускается:
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 { }
так что нет смысла так писать.
Я не уверен, но самое простое объяснение такого поведения, которое я могу придумать, заключается в том, что пустой ранний инициализатор просто оптимизируется, поэтому он эквивалентен val baseFail = new Base
а также val proxy = new Base with Proxy
, new Base
не с тем же сообщением об ошибке, и new Base with Proxy
законно, потому что это анонимный класс. Если так, то я думаю, что это технически ошибка компилятора, но довольно незначительная.