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