11
object ScalaTrueRing {
  def rule = println("To rule them all")
}

这段代码会被编译成java字节码,如果我反编译,那么等效的java代码是这样的:

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;
/*    */   }
/*    */ }

它被编译成两个类,如果我使用Scala.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方法呢?

4

3 回答 3

12

重要的是要理解,在 scala 中,anobject实际上是一等公民:它是一个可以作为任何其他对象传递的实际实例。举例:

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 )

与静态方法不同,我们的单例对象具有完全的多态行为。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 中,您不能在同一个类中拥有具有相同签名的实例方法和静态方法。不过可能还有其他原因。

于 2012-10-08T15:55:43.090 回答
3

解释自“Scala 中的编程” - 因为 Scala 伴随对象(单例对象)不仅仅是静态方法的持有者。通过作为不同 Java 类的实例,它允许开发人员扩展单例对象和混合特征。这不能用静态方法来完成。

于 2012-10-08T15:37:53.457 回答
1

这篇博客文章“看看 Scala 如何编译为 Java”应该会回答您的问题

通常 ClassName$.class 是内部类的结果 - Scala 显然略有不同。

于 2012-10-08T14:45:05.700 回答