0

I asked a question earlier: here and although I accepted the answer I'm still very far from actually understanding, so I dug a bit deeper and I'm writing a further question.

The behaviour of overriding val in scala surprises me. For example, given this code:

class A {
  val name = "AAAAA"
}

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

if I say:

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

I expect

BBBBB
AAAAA

but I get

BBBBB
BBBBB

I'm just trying to see the AAAAA value that I think A should be storing somewhere. So I try:

class A {
  val name = "AAAAA"

  def showSuper {
    println(name)
  }
}

and:

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

but I still get:

BBBBB
BBBBB

So I try do have a look at what scala is actually generating from my classes:

scalac -Xprint:all A.scala

gives me

  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";
      ()
    }
  }

The call to B.super happens before B.this.name even gets set and A clearly sets its name to AAAAA.

What is going on? Why, when I override a val like this, can I not see A's value (or is it getting set to B's value?) What is the mechanism by which this happens? How can I see this mechanism - is there a piece of scala source code that shows me why this happens?

Many thanks

EDIT: Meant to add that if I use javap to look at the bytecode, it clearly shows that A and B each have their own copy of the name variable:

$ 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();
}

So it's not like A and B have to be sharing the same variable - they each have the potential for using their own copy.

Community
  • 1
  • 1
Bruce
  • 2,406
  • 5
  • 29
  • 35
  • [This answer](http://stackoverflow.com/questions/12358426/how-to-use-asinstanceof-properly-in-scala) might be informative – bwroga Nov 13 '13 at 13:48
  • Thanks, but I don't think it's anything to do with casting. In my example above, even b.showSuper I would expect to show AAAAA because it's explicitly calling its superclass's method in which A's field is referenced (or so it seems to me at least) – Bruce Nov 13 '13 at 13:52

2 Answers2

2

That is not Scala specific. It is OOP question. The second class override the method and hides it. Overriding and Hiding Methods

baltov
  • 194
  • 5
  • Hmm.. what's puzzling me is that this is about a variable not a method. In java, if you cast a variable to its superclass, you can access the superclass's field - for example ((A)b).showField1(); and println(((A)b).field1) are different. The first still calls the subclass's method, but the second accesses the SUPERCLASS's field. So perhaps the answer to my question is that scala treats ALL field access as method accesses with the override rules that apply to methods in java rather than fields? – Bruce Nov 13 '13 at 14:06
  • 2
    no. it is method. See java code: public java.lang.String name(); Variable name is private, method name() is public. – baltov Nov 13 '13 at 14:10
  • Ahh... if I understand you right, you're saying that even though I've casted to the superclass, because the superclass calls a 'name' method, due to polymorphism it is still going to call the 'name' method on the subclass? That really makes sense! Thank you so much, this was driving me mad. I also found this helpful post http://stackoverflow.com/questions/14912847/interesting-behavior-of-calling-method-after-casting-a-subclass-to-a-super-class with the helpful rule 'when you call a method, it will be on instance type (polymorphism) When you call variable, it will be on Type of the reference' – Bruce Nov 13 '13 at 14:19
  • 2
    The Uniform Access Principle states that it shouldn't matter whether an answer is given to you by returning canned data (a field) or computing it on the fly (a method). The two *must* behave exactly identical. It simply doesn't make sense to treat them differently. A method which always returns the same value is isomorphic with a field. They are indistinguishable. The fact that Java treats them differently is a major design flaw. – Jörg W Mittag Nov 13 '13 at 14:20
1

Just some extra notes. It transpires that if you decompile the scala classes, you find that scala changes references to vals to references to the methods that get the vals. For example where my class A above has:

def showSuper {
  println(name)
}

the decompiled bytecode shows

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

(thanks to jd-gui)

So this makes it clear that scala references to vals are equivalent in bytecode to java's polymorphic method calls rather than java's variables which are type dependent.

Bruce
  • 2,406
  • 5
  • 29
  • 35