2012-10-08 22 views
6
object ScalaTrueRing { 
    def rule = println("To rule them all") 
} 

這一段代碼會被編譯成Java字節碼,如果我反編譯它,那麼相應的Java代碼與此類似:爲什麼Scala伴侶對象被編譯成兩個類(Java和.NET編譯器)?

public final class JavaTrueRing 
{ 
    public static final void rule() 
    { 
    ScalaTrueRing..MODULE$.rule(); 
    } 
} 


/* */ public final class JavaTrueRing$ 
/* */ implements ScalaObject 
/* */ { 
/* */ public static final MODULE$; 
/* */ 
/* */ static 
/* */ { 
/* */  new(); 
/* */ } 
/* */ 
/* */ public void rule() 
/* */ { 
/* 11 */  Predef..MODULE$.println("To rule them all"); 
/* */ } 
/* */ 
/* */ private JavaTrueRing$() 
/* */ { 
/* 10 */  MODULE$ = this; 
/* */ } 
/* */ } 

它編成兩個班,如果我使用斯卡拉.NET編譯器,它會被編譯成MSIL代碼,相當於C#代碼是這樣的:

public sealed class ScalaTrueRing 
{ 
    public static void rule() 
    { 
     ScalaTrueRing$.MODULE$.rule(); 
    } 
} 

[Symtab] 
public sealed class ScalaTrueRing$ : ScalaObject 
{ 
    public static ScalaTrueRing$ MODULE$; 
    public override void rule() 
    { 
     Predef$.MODULE$.println("To rule them all"); 
    } 
    private ScalaTrueRing$() 
    { 
     ScalaTrueRing$.MODULE$ = this; 
    } 
    static ScalaTrueRing$() 
    { 
     new ScalaTrueRing$(); 
    } 
} 

它也編成兩個班。

爲什麼Scala編譯器(用於Java和.NET的編譯器)執行此操作? 爲什麼不在靜態規則方法中調用println方法?

+0

那麼,類JavaTrueRing $'從哪裏來? –

+0

@The Elite Gentleman它的真名是ScalaTrueRing $,我更改了名稱以明確表明它是java代碼。我反編譯了這個類文件並得到了。 – CuiPengFei

回答

8

重要的是要明白,在斯卡拉,object實際上是一流的公民:它是一個實際的實例,可以作爲任何其他對象傳遞。 例如:

trait Greetings { 
    def hello() { println("hello") } 
    def bye() { println("bye") } 
} 

object FrenchGreetings extends Greetings { 
    override def hello() { println("bonjour") } 
    override def bye() { println("au revoir") } 
} 

def doSomething(greetings: Greetings) { 
    greetings.hello() 
    println("... doing some work ...") 
    greetings.bye() 
} 

doSomething(FrenchGreetings) 

不像靜態方法,我們的單對象有充分的多態性beheviour。 doSomething確實會調用我們重寫hellobye方法,而不是默認的實現:

bonjour 
... doing some work ... 
au revoir 

所以object實施必然是一個正確的類。但是爲了與java的互操作性,編譯器還生成靜態方法,該方法只轉發到該類的唯一實例(MODULE$)(請參閱JavaTrueRing.rule())。 這樣,一個java程序就可以像普通的靜態方法一樣訪問單例對象的方法。 現在您可能會問,爲什麼scala不會將靜態方法轉發器與實例方法放在同一個類中。這給我們的東西,如:

public final class JavaTrueRing implements ScalaObject { 
    public static final MODULE$; 

    static { 
    new JavaTrueRing(); 
    } 

    public void rule() { 
    Predef.MODULE$.println("To rule them all"); 
    } 

    private JavaTrueRing() { 
    MODULE$ = this; 
    } 

    // Forwarders 
    public static final void rule() { 
    MODULE$.rule(); 
    } 
} 

我認爲,最主要的原因,這不能簡單是因爲在JVM不能在同一個班級有問心無愧相同的實例方法和靜態方法簽名。 雖然可能有其他原因。

1

Blog entry "A Look at How Scala Compiles to Java"應該回答你的問題

典型的類名$的.class是內部類的結果 - 斯卡拉顯然略有不同。

+1

博客文章解釋了Scala編譯器的功能,但並不能解釋原因。爲什麼Scala編譯器不能在靜態規則方法中調用println方法? – CuiPengFei

3

從「Programming in Scala」中解釋 - 因爲scala伴侶對象(單例對象)不僅僅是靜態方法的持有者。通過成爲不同Java類的實例,它允許開發人員擴展單例對象和混入特性。這不能用靜態方法完成。

相關問題