views:

89

answers:

2

Say I have:

class A(val foo: String)

class B(foo: String) extends A(foo)
class C(val foo: String) extends A(foo)
class D(override val foo: String) extends A(foo)
class E(bar: String) extends A(bar)

I'm interested in how much memory instances of each of these classes take up. Instances of class A will have a single member variable: foo.

How about classes B,C,D and E? How many member variables will they each have? I suspect E will have two (E.bar, A.foo), I expect D will have one (A.foo), but I wonder about B and C, might they have two? (B.foo, A.foo)?

+4  A: 

You class C will require an override keyword on val foo to compile, rendering it identical to D. It will store its own copy of foo. You class B does not add storage unless someplace outside its constructor body there is a reference to foo. That would force a hidden field to be created to hold the constructor parameter. The constructor body is all the code within the class definition and outside any method body.

Addendum:

package storage

    class A(val foo: String)

    class B(             foo: String) extends A(foo)
//  class C(         val foo: String) extends A(foo)
    class D(override val foo: String) extends A(foo)
    class E(             bar: String) extends A(bar)
    class F(             bar: String) extends A(bar) { def barbar: String = bar }

I am perplexed by this:

% javap -private storage.F
Compiled from "Storage.scala"
public class storage.F extends storage.A implements scala.ScalaObject{
    public java.lang.String barbar();
    public storage.F(java.lang.String);
}

What is method barbar using to get its return value?

Randall Schulz
`B` would also not add a field for `foo` in case you accessed it outside of the constructor body but use the parent's accessor.
Moritz
Thanks for the info Randall. Just to clarify: are you saying that instances of D will have two copies of foo?
Alex Black
@Alex Black: Yes. The `javap` command is good for disclosing these details, but be aware that you need to use its `-private` option to see all the private fields that back the accessor (and mutator) methods that the compiler uses to mediate access to those fields.
Randall Schulz
+4  A: 

All of the examples that compile (A, B, D, E) take exactly the same amount of storage space. In fact, even

class F(val bar: String) extends A(bar)

will have the data stored in one field--it just gets an extra accessor method for the same field. However, if you

class G(var bar: String) extends A(bar)

then a new field is constructed.

You can check all this by compiling your examples above and looking at the bytecode from javap -c Classname (note the putfield at 2: in the constructor of A):

public class Sizes$A extends java.lang.Object implements scala.ScalaObject{
public java.lang.String foo();
  Code:
   0:   aload_0
   1:   getfield    #11; //Field foo:Ljava/lang/String;
   4:   areturn

public Sizes$A(java.lang.String);
  Code:
   0:   aload_0
   1:   aload_1
   2:   putfield    #11; //Field foo:Ljava/lang/String;
   5:   aload_0
   6:   invokespecial   #18; //Method java/lang/Object."<init>":()V
   9:   return    
}

(And the lack of an extra putfield in F...)

public Sizes$F(java.lang.String);
  Code:
   0:   aload_0
   1:   aload_1
   2:   invokespecial   #15; //Method Sizes$A."<init>":(Ljava/lang/String;)V
   5:   return

(And the presence of one again in G...)

public Sizes$G(java.lang.String);
  Code:
   0:   aload_0
   1:   aload_1
   2:   putfield    #11; //Field bar:Ljava/lang/String;
   5:   aload_0
   6:   aload_1
   7:   invokespecial   #18; //Method Sizes$A."<init>":(Ljava/lang/String;)V
   10:  return
Rex Kerr