我有一个包装类,可以存储Long,Double或Boolean原始值(以及为简单起见我已删除的其他一些东西)。我原来的天真实现只是将包装的值存储在Any类型的字段中,这导致值被装箱。
要删除装箱并减少内存使用量,我尝试使用泛型,但了解到由于类型擦除,这不会保存任何东西。所以我尝试使用@specialized,但得到了令人惊讶的结果。
以下代码是使用scalac 2.9.3构建的,并在JDK7上运行。 MemoryMeasurer来自here,我相信它是准确的。 “填充”字段不重要;我只是用它来填充基础对象(没有包装的值)到16个字节,所以我的各种尝试的效果更清楚。
import objectexplorer.MemoryMeasurer
class GenericNonSpecialized[A] (wrapped: A, val padding: Int) {
def getWrapped: Any = wrapped
}
class GenericSpecialized[@specialized(Long, Double, Boolean) A] (wrapped: A, val padding: Int) {
def getWrapped: A = wrapped
}
class GenericSpecializedVal[@specialized(Long, Double, Boolean) A] (val wrapped: A, val padding: Int) {
def getWrapped: A = wrapped
}
class NonGeneric(val wrapped: Long, padding: Int) {
}
object App {
def main(args: Array[String]) {
println(MemoryMeasurer.measureBytes(new GenericNonSpecialized(4L, 0)))
// Expect: 48: NonSpecialized object (24 bytes) + boxed long (24 bytes)
// Actual: 48
// I expect all of the below to be 24 bytes: Object overhead (12 bytes) + Long (8 bytes) + Int (4 bytes),
// but only the non-generic one is actually 24 bytes.
println(MemoryMeasurer.measureBytes(new GenericSpecialized(4L, 0))) // 56
println(MemoryMeasurer.measureBytes(new GenericSpecializedVal(4L, 0))) // 32
println(MemoryMeasurer.measureBytes(new NonGeneric(4L, 0))) // 24
}
}
问题:
答案 0 :(得分:7)
不幸的是,专门的类从其非专用父级继承,并且该类包含用于装箱副本的存储空间。因此,简短的答案是你不能用这种方式形成一个有效的包装器。
您可以在特征中声明数据:
trait Boxer[@specialized A]{ def boxed: A }
然后手动提供实现:
class BoxerInt(val boxed: Int) extends Boxer[Int]
class BoxerDouble(val boxed: Double) extends Boxer[Double]
然后编写Boxer伴侣来重载apply方法:
object Boxer {
def apply(i: Int) = new BoxerInt(i)
def apply(d: Double) = new BoxerDouble(d)
}
这样你就可以让它看起来像你不必做所有这些工作:
val box = Boxer(5.0)
但它与其他专业化用途仍然不完全无缝(特别是在通用上下文中创建将始终是一个问题)。