2015-06-12 22 views
12

Diciamo che abbiamo la seguente struttura di classe in Scala.Accesso a classi nidificate di Scala da Java

object Foo { 
    class Bar 
} 

Possiamo facilmente costruire Bar in Java con new Foo.Bar(). Ma tutto cambia quando aggiungiamo un ulteriore livello di classi annidate.

object Foo { 
    object Bar { 
    class Baz 
    } 
} 

In qualche modo, non è più possibile costruire la classe più interna Baz in Java. Guardando l'output javap, non riesco a vedere alcuna differenza significativa tra il primo (2 livelli) e il secondo caso (3 livelli). Il codice generato mi sembra abbastanza ragionevole.

2 piani:

public class Foo$Bar { ... } 

3 piani

public class Foo$Bar$Baz { ... } 

Detto questo, qual è la differenza tra i 2 livelli rispetto a 3 livelli nidificati classi Scala quando si accede da Java?

risposta

12

Diamo le due versioni differenti nomi per renderli un po 'più facile parlare di:

object Foo1 { 
    class Bar1 
} 

object Foo2 { 
    object Bar2 { 
    class Baz2 
    } 
} 

Ora, se si guardano i file di classe, vedrete che il compilatore Scala ha creato una classe Foo1 . Quando si esegue javap -v su Foo1$Bar1, vedrai che questa classe è elencato come classe contenitrice:

InnerClasses: 
    public statiC#14= #2 of #13; //Bar1=class Foo1$Bar1 of class Foo1 

Questo è esattamente ciò che accadrebbe con una classe innestata statica in Java, in modo che il compilatore Java è perfettamente felice di compilare new Foo1.Bar1() per te.

Ora guardate la javap -v output per Foo2$Bar2$Baz2:

InnerClasses: 
    public statiC#16= #13 of #15; //Bar2$=class Foo2$Bar2$ of class Foo2 
    public statiC#17= #2 of #13; //Baz2=class Foo2$Bar2$Baz2 of class Foo2$Bar2$ 

Ora classe contenitrice è Foo2$Bar2$, non Foo2$Bar2 (in realtà il compilatore Scala non ha nemmeno genera un Foo2$Bar2 a meno che non si aggiunge una classe compagno per object Bar2). Il compilatore Java si aspetta che una classe interna statica Baz2 di una classe che include Foo2$Bar2$ sia denominata Foo2$Bar2$$Baz2, con due segni di dollaro. Questo non corrisponde a quello che è effettivamente ottenuto (Foo2$Bar2$Baz2), quindi dice no a new Foo2.Bar2.Baz2().

Java è perfettamente felice di accettare i segni del dollaro nei nomi delle classi, e in questo caso poiché non può capire come interpretare Foo2$Bar2$Baz2 come una classe interna di qualche tipo, ti consente di creare un'istanza con new Foo2$Bar2$Baz2(). Quindi questa è una soluzione, ma non molto carina.

Perché il compilatore Scala trattare Foo1 e Bar2 modo diverso (nel senso che Bar2 non ottiene una classe Bar2), e perché la classe racchiude elencati l'attributo InnerClasses per Baz2 avere un simbolo del dollaro alla fine, mentre quello per Bar1 no? Non ne ho davvero idea. Ma questa è la differenza: basta un po 'più di verbosità per vederlo con javap.