Ранний инициализатор `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 законно, потому что это анонимный класс. Если так, то я думаю, что это технически ошибка компилятора, но довольно незначительная.

Другие вопросы по тегам