为什么Scala伴侣对象被编译为两个类(Java和.NET编译器)?

Cui*_*崔鹏飞 9 .net java compiler-construction scala companion-object

object ScalaTrueRing {
  def rule = println("To rule them all")
}
Run Code Online (Sandbox Code Playgroud)

这段代码将被编译成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;
/*    */   }
/*    */ }
Run Code Online (Sandbox Code Playgroud)

它被编译成两个类,如果我使用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$();
    }
}
Run Code Online (Sandbox Code Playgroud)

它也被编译成两个类.

为什么Scala编译器(用于Java的编译器和用于.NET的编译器)执行此操作?为什么不在静态规则方法中调用println方法呢?

Rég*_*les 12

重要的是要理解在scala中,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 )
Run Code Online (Sandbox Code Playgroud)

与静态方法不同,我们的单例对象具有完整的多态性行为.doSomething确实会调用我们的override hellobye方法,而不是默认实现:

bonjour
... doing some work ...
au revoir
Run Code Online (Sandbox Code Playgroud)

因此,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();
  }  
}
Run Code Online (Sandbox Code Playgroud)

我认为这不能简单的主要原因是因为在JVM中你不能在同一个类中拥有一个实例方法和一个静态方法具有相同的签名.可能还有其他原因.