假设我们有以下案例类:
abstract sealed class Tree
case class Leaf(i: Int) extends Tree
case class Node(left: Tree, right: Tree) extends Tree
每次调用case类构造函数时,都会在内存中创建一个新对象。例如,在下面的代码中:
val a = Leaf(0)
val b = Leaf(0)
a和b指向内存中的不同对象:
a == b // true
a eq b // false
我想覆盖" apply"案例类的方法,使它们返回一个缓存的对象,如果它已经存在,那么,在上面的最小例子中," a eq b"会回归真实。
我在Stackoverflow中找到了这两个相关的答案:
How to override apply in a case class companion(显示如何覆盖"应用"方法)
Why do each new instance of case classes evaluate lazy vals again in Scala?(显示缓存案例类实例的简单方法)
我计划实施我的首要任务" apply"以一种结合上面链接的两种方法的方式进行缓存的方法。但我想知道是否有其他方法我应该考虑。如果您知道,请在这里分享您的解决方案吗?
缓存案例类的实例似乎是减少内存消耗的一个非常有用和自然的事情。然而,我计划实施的解决方案(基于上面链接的两个答案)似乎相当复杂,需要大量的样板代码,这将损害案例类的优雅和简洁。有谁知道Scala语言的未来版本是否允许我们通过编写像这样简单的东西来实现案例类实例缓存:
abstract sealed class Tree
cached case class Leaf(i: Int) extends Tree
cached case class Node(left: Tree, right: Tree) extends Tree
...
答案 0 :(得分:2)
缓存案例类的实例似乎是减少内存消耗的一个非常有用和自然的事情。
请注意,这甚至不是远程自动改进,而且在很大程度上取决于案例类的使用模式(不仅仅是您的使用模式,还有使用您库的任何人):
您需要考虑内存缓存需求和无法从缓存中引用的垃圾收集实例(请注意,使用WeakHashMap
无法获得帮助:它需要"值对象不直接或间接地强烈引用自己的密钥")。
如果键是基元(如Leaf
中所示),则需要在查找之前将它们装箱,这通常已经是构造函数调用。
地图中的查找比一个简单的构造函数调用要慢得多。
逃生分析通常会确保对象不是实际构建的,同时确保您的程序就好像一样。当然,缓存将确保对象做转义。
但是忽略了这一切,你可以编写一个宏注释,它允许你@cached case class Leaf(i: Int) extends Tree
并生成你想要的代码(或至少@cachedcase class
;我不确定你是否会成为能够覆盖apply
否则)。由于上述原因,我不希望它很快成为语言的一部分。