说我有:
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)
我对每个类的内存实例占用多少感兴趣。 A类实例将只有一个成员变量:foo。
班级B,C,D和E怎么样?他们各有几个成员变量?我怀疑E会有两个(E.bar,A.foo),我希望D会有一个(A.foo),但我想知道B和C,他们可能有两个吗? (B.foo,A.foo)?
答案 0 :(得分:14)
所有编译的示例(A
,B
,D
,E
)都占用了相同数量的存储空间。事实上,甚至
class F(val bar: String) extends A(bar)
将数据存储在一个字段中 - 它只是为同一个字段获取一个额外的存取方法。但是,如果你
class G(var bar: String) extends A(bar)
然后构建一个新字段。
您可以通过编译上面的示例并查看javap -c Classname
中的字节码来检查所有这些(请注意2:
构造函数中A
处的putfield:
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
}
(F
中没有额外的putfield ......)
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
(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
答案 1 :(得分:7)
您的课程C
需要override
上的val foo
关键字进行编译,使其与D
相同。它将存储自己的foo副本。您的类B
不会添加存储,除非在其构造函数体之外的某个位置存在对foo
的引用。这将强制创建隐藏字段以保存构造函数参数。构造函数体是类定义中的所有代码,位于任何方法体之外。
附录:
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 }
我为此感到困惑:
% 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);
}
方法barbar
用什么来获取其返回值?