2016-12-16 1 views
3

Il semble y avoir une subtilité lors de l'utilisation de la syntaxe d'initialisation précoce.L'initialisateur initial `new {} avec SomeTrait` échoue

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? 

Pourquoi la ligne baseFail échouent, tandis que les autres ne le font pas val s?

Le message d'erreur est également source de confusion - Je ne cherche pas à instancier Base, mélanger seulement dans

Répondre

5

Lorsque vous écrivez new { } with Base, techniquement ne sont pas aucune définition précoce. Selon le SLS 5.1.6, le compilateur recherche un modèle comme celui-ci:

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

Bien qu'il ne dit pas explicitement ce qui se passe lorsque les séquences de définitions est vide, il semble juste enlever, parce que quand vous compilez val a = new { val x = 1 } with Base, vous obtenez quelque chose comme ça après la phase parsing:

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

Mais si vous videz les accolades, vous obtenez simplement:

val a = new Base() 

ce qui est malade Egal, comme c'est new Base.


Pour résumer:

est une classe anonyme qui est permis :

val base1 = new Base { } 

Simplifie à new Base, ce qui est illégal:

val baseFail = new { } with Base 

Est-ce un bon début définition (non vide) qui est autorisée:

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

Simplifie à new Base with Proxy qui est également admis:

val proxy = new { } with Base with Proxy 

new { } with Base { } compile, mais il est exactement le même que celui new Base { }, donc il n'y a aucune raison de l'écrire de cette façon.

1

Je ne suis pas sûr, mais l'explication la plus simple que je peux penser de ce comportement est que le vide au début. L'initialiseur est simplement optimisé, c'est donc équivalent à val baseFail = new Base et val proxy = new Base with Proxy. new Base échoue avec le même message d'erreur et new Base with Proxy est légal car il s'agit d'une classe anonyme. Si c'est le cas, je pense que c'est techniquement un bug de compilateur, mais assez mineur.