鉴于以下内容:
class TestClass extends TestTrait {
def doesSomething() = methodValue + intValue
}
trait TestTrait {
val intValue = 4
val unusedValue = 5
def methodValue = "method"
def unusedMethod = "unused method"
}
Run Code Online (Sandbox Code Playgroud)
当上面的代码运行时,TestClass实际上是否将内存分配给unusedValue或unusedMethod?我已经使用了javap并且我知道存在一个unusedValue和一个unusedMethod,但我无法确定它们是否实际上填充了任何类型的状态或内存分配.
基本上,我试图理解一个类总是得到一个特征提供的所有,或者编译器是否足够聪明,只提供该类实际使用的特征?
如果一个特质总是强加于一个类,它似乎可能是低效的,因为我希望许多程序员将使用特征作为mixins,因此在任何地方浪费内存.
感谢所有阅读并帮助我深究这一点的人!
一般来说,在Scala,Java和C++等语言中,每个类都有一个指向其实例方法的指针表.如果你的问题是Scala编译器是否会在方法表中分配插槽,unusedMethod那么我会说是的.
我想你的问题是Scala编译器是否会查看主体TestClass并说"哇,我只看到使用methodValue和intValue,所以作为一个好的编译器,我将避免在TestClass方法表中分配空间unusedMethod.但它通常不能真正做到这一点.原因是,TestClass将编译成一个类文件TestClass.class,这个类可能由程序员在你不熟悉的程序员中使用.
他们想和你的班级做什么?这个:
var x = new TestClass();
print(x.unusedMethod)
Run Code Online (Sandbox Code Playgroud)
看,问题是编译器无法预测将来会使用这个类的人,因此它将所有方法放入其方法表中,甚至是那些未被类中其他方法调用的方法.这适用于在类中声明的方法或通过实现的特征拾取的方法.
如果您希望编译器在固定的封闭系统上进行全局系统范围的静态分析和优化,那么我认为理论上它可以减少这些事情,但我怀疑这将是一个非常昂贵的优化,并不值得.如果您需要这种节省内存的话,最好自己编写较小的特性.:)
考虑Scala如何在JVM级别实现traits可能是最容易的:
值得注意的是假设var bippy: Int是如何在等效的java中实现的:
private int bippy; //backing field
public int bippy() { return this.bippy; } //getter
public void bippy_$eq(int x) { this.bippy = x; } //setter
Run Code Online (Sandbox Code Playgroud)
对于a val,后备字段是final,不会生成setter
混合特征时,编译器不会分析使用情况.首先,这会破坏界面的合同.执行这样的分析也需要不可接受的长时间.这意味着您将始终从混入的任何val/vars继承支持字段的成本.
正如您已经暗示的那样,如果这是一个问题,那么解决方案就是def在您的特征中使用s.
这种方法还有其他一些好处,并且由于统一访问原则,如果需要,您可以始终val在继承层次结构中进一步覆盖这样的方法.