【问题标题】:Why is a Scala companion object compiled into two classes(both Java and .NET compilers)?为什么将 Scala 伴随对象编译为两个类(Java 和 .NET 编译器)?
【发布时间】:2012-10-08 14:40:17
【问题描述】:
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方法呢?

【问题讨论】:

  • 那么,JavaTrueRing$ 这个类是从哪里来的呢?
  • @精英绅士它的真名是ScalaTrueRing$,我更改了名称以明确表明它是java代码。我反编译了类文件并得到了它。

标签: java .net scala compiler-construction companion-object


【解决方案1】:

重要的是要理解,在 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 )

与静态方法不同,我们的单例对象具有完全的多态行为。 doSomething 确实会调用我们覆盖的 hello 和 bye 方法,而不是默认实现:

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 中,您不能在同一个类中拥有具有相同签名的实例方法和静态方法。 不过可能还有其他原因。

【讨论】:

    【解决方案2】:

    从“Scala 编程”中转述——因为 Scala 伴随对象(单例对象)不仅仅是静态方法的持有者。通过作为不同 Java 类的实例,它允许开发人员扩展单例对象和混合特征。静态方法无法做到这一点。

    【讨论】:

      【解决方案3】:

      这个Blog entry "A Look at How Scala Compiles to Java"应该回答你的问题

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

      【讨论】:

      • 那篇博文解释了 Scala 编译器的作用,但没有解释原因。为什么Scala编译器不直接调用静态规则方法中的println方法?
      猜你喜欢
      • 2021-12-06
      • 2017-07-01
      • 2018-12-02
      • 2020-06-30
      • 1970-01-01
      • 1970-01-01
      • 2015-08-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多