7

我在玩 Scala。我发现了 3 个有趣的东西(标题是第三个)。

1 声明为 val 的局部变量不会被解释为 final。

class HowAreVarAndValImplementedInScala {
  var v1 = 123
  val v2 = 456

  def method1() = {
    var v3 = 123
    val v4 = 456
    println(v3 + v4)
  }
}

如果我将上面的 scala 代码编译成字节码,然后将其反编译成 java,它看起来像这样:

public class HowAreVarAndValImplementedInScala
{
  private int v1 = 123;
  private final int v2 = 456;

  public int v1()
  {
    return this.v1;
  }

  public void v1_$eq(int x$1) { this.v1 = x$1; }

  public int v2() { return this.v2; }

  public void method1() {
    int v3 = 123;
    int v4 = 456;
    Predef..MODULE$.println(BoxesRunTime.boxToInteger(v3 + v4));
  }
}

我们可以看到 v2 是最终版本,但 v4 不是,为什么?

.net 的 2 scala 编译器为许多(如果不是全部)公共实例方法添加了 override 关键字

如果我们把上面显示的scala代码编译成CIL,然后反编译成C#,是这样的:

public class HowAreVarAndValImplementedInScala : ScalaObject
{
  private int v1;
  private int v2;

  public override int v1()
  {
    return this.v1;
  }

  public override void v1_$eq(int x$1)
  {
    this.v1 = x$1;
  }

  public override int v2()
  {
    return this.v2;
  }

  public override void method1()
  {
    int v3 = 123;
    int v4 = 456;
    Predef$.MODULE$.println(v3 + v4);
  }

  public HowAreVarAndValImplementedInScala()
  {
    this.v1 = 123;
    this.v2 = 456;
  }
}

所有公共实例方法(不包括构造函数)都被标记为覆盖,为什么?有必要吗?

3 .net 的 Scala 编译器失去了 val 的含义

在上面的c#代码中,我们可以看到v2只是一个普通的字段,而在java couter部分,v2被标记为final,不应该被Scala编译器为.net标记为readonly吗?(一个bug? )

4

1 回答 1

7

在字节码级别,局部变量不存在 final 。事实上,局部变量的概念本身也不存在。将局部变量标记为 final 纯粹是为了编译时检查。由于类文件中不存在该信息,因此反编译器无法猜测它。

至于后两个问题,我对 CIL 字节码不太熟悉,但如果我不得不猜测,我会说没有理由不添加覆盖,并且readonly可能有不同的语义。

编辑:查看 CIL 规范后,这就是我发现的。

finalJava 的字段标志的 CIL 等效项是initonly,它似乎具有相同的语义。目前尚不清楚为什么 Scala 编译器不发出这个。也许他们只是没有解决这个问题?或者您使用的 .net 反编译器可能没有反映这一点。如果您想查看编译器实际生成的内容,最好直接查看字节码。

于 2013-05-08T17:24:40.190 回答