请帮助我理解scala中val的覆盖

时间:2013-11-13 13:43:59

标签: scala

我之前问了一个问题:here虽然我接受了答案,但我仍然远离实际的理解,所以我挖得更深一些,我正在写另一个问题。

在scala中覆盖val的行为让我感到惊讶。例如,给定此代码:

class A {
  val name = "AAAAA"
}

class B extends A {
  override val name = "BBBBB"
}

如果我说:

object Atest extends App {
  val b = new B
  println(b.name)
  val a = b.asInstanceOf[A]
  println(a.name)
}

我希望

BBBBB
AAAAA

但我得到

BBBBB
BBBBB

我只是想看看我认为应该存储在某处的AAAAA值。所以我试试:

class A {
  val name = "AAAAA"

  def showSuper {
    println(name)
  }
}

  val b = new B
  val a = b.asInstanceOf[A]
  b.showSuper
  a.showSuper

但我仍然得到:

BBBBB
BBBBB

所以我试着看一下scala实际上从我的类生成的内容:

scalac -Xprint:all A.scala

给了我

  class A extends Object {
    private[this] val name: String = _;
    <stable> <accessor> def name(): String = A.this.name;
    def <init>(): p3.A = {
      A.super.<init>();
      A.this.name = "AAAAA";
      ()
    }
  };
  class B extends p3.A {
    private[this] val name: String = _;
    override <stable> <accessor> def name(): String = B.this.name;
    def <init>(): p3.B = {
      B.super.<init>();
      B.this.name = "BBBBB";
      ()
    }
  }

对B.super的调用发生在B.this.name甚至设置之前,A明确地将其名称设置为AAAAA。

发生了什么事?为什么,当我覆盖这样的val时,我看不到A的值(或者它是否设置为B的值?)这种情况发生的机制是什么?我怎么能看到这种机制 - 是否有一段scala源代码向我展示了为什么会发生这种情况?

非常感谢

编辑: 我想补充一点,如果我使用javap来查看字节码,它清楚地表明A和B都有自己的名称变量副本:

$ javap -private A
Compiled from "A.scala"
public class p3.A extends java.lang.Object{
    private final java.lang.String name;
    public java.lang.String name();
    public p3.A();
}

$ javap -private B
Compiled from "A.scala"
public class p3.B extends p3.A{
    private final java.lang.String name;
    public java.lang.String name();
    public p3.B();
}

所以它不像A和B必须共享相同的变量 - 它们都有可能使用自己的副本。

2 个答案:

答案 0 :(得分:2)

这不是Scala特有的。这是OOP问题。第二个类重写该方法并隐藏它。 Overriding and Hiding Methods

答案 1 :(得分:1)

只是一些额外的笔记。可以看出,如果您对scala类进行反编译,则会发现scala将对val的引用更改为对获取val的方法的引用。例如,我上面的A类有:

def showSuper {
  println(name)
}

反编译的字节码显示

public void showSuper()
{
  Predef..MODULE$.println(name());
}

(感谢jd-gui

因此,这清楚地表明,对于val的scala引用在字节码中与java的多态方法调用相同,而不是与类型相关的java变量。