0

我之前问了一个问题:在这里,虽然我接受了答案,但我离真正理解还很远,所以我挖得更深一些,我正在写一个进一步的问题。

在 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

我只是想看看我认为 A 应该存储在某处的 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 每个都有自己的 name 变量副本:

$ 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 不必共享同一个变量——他们每个人都有可能使用自己的副本。

4

2 回答 2

2

这不是 Scala 特有的。这是面向对象的问题。第二个类覆盖该方法并将其隐藏。 覆盖和隐藏方法

于 2013-11-13T13:57:53.403 回答
1

只是一些额外的注释。事实证明,如果您反编译 scala 类,您会发现 scala 将对 vals 的引用更改为对获取 vals 的方法的引用。例如,我上面的 A 类有:

def showSuper {
  println(name)
}

反编译的字节码显示

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

(感谢jd-gui

所以这清楚地表明,scala 对 val 的引用在字节码中等同于 java 的多态方法调用,而不是依赖于类型的 java 变量。

于 2013-11-13T21:45:11.657 回答