Class 如何实现案例类的实例共享
假设定义:Class 如何实现案例类的实例共享,class,scala,case-class,Class,Scala,Case Class,假设定义: case class IntegerWrapper(i : Int) 在这种情况下,可能会创建大量具有i=[0..N>的integerrapper实例,您需要做什么来: 将此范围映射到一组固定的单例[IntegerWrapper(0)…IntegerWrapper(N)> 保留类IntegerRapper的现有值语义(匹配、等于、哈希代码、序列化) 我正在寻找类似于java.lang.Integer的实例共享。我想我的问题是,这是否可以在不必自己做任何事情的情况下完成。简单地用ap
case class IntegerWrapper(i : Int)
在这种情况下,可能会创建大量具有i=[0..N>
的integerrapper
实例,您需要做什么来:
[IntegerWrapper(0)…IntegerWrapper(N)>
IntegerRapper
的现有值语义(匹配、等于、哈希代码、序列化)java.lang.Integer
的实例共享。我想我的问题是,这是否可以在不必自己做任何事情的情况下完成。简单地用apply(I:Int)
定义一个伴生对象不会编译。有什么建议吗?类似的东西
sealed trait IntegerWrapper {
def i: Int
}
object IntegerWrapper extends (Int => IntegerWrapper) {
private case class IntegerWrapperImpl(i: Int) extends IntegerWrapper
private val instances: List[IntegerWrapperImpl] = ...
/* Wrapper instances for i in [0..N) */
def apply(i: Int): IntegerWrapper = instances(i)
def unapply(iw: IntegerWrapper): Option[Int] = Some(iw.i)
}
优点是equals
和hashCode
仍然是由编译器生成的,因为integerrappermpl
是一个case类。缺点是您不能直接使用其他编译器添加的case类好东西,例如copy
。如果您想使用它,可以公开integerrappermpl
到客户端,或者最好将copy
添加到IntegerWrapper
界面
模式匹配照常工作:
val iw0 = IntegerWrapper(0)
val iw1: IntegerWrapper = IntegerWrapper(1)
iw0 match {
case IntegerWrapper(0) => println("IW(0)")
case _ => println("something else")
} // IW(0)
iw1 match {
case IntegerWrapper(1) => println("IW(1)")
case _ => println("something else")
} // IW(1)
iw1 match {
case IntegerWrapper(2) => println("IW(2)")
case _ => println("something else")
} // something else
这与scala的
Symbol
类的用例基本相同。因此,您可以看看Symbol.scala,作为合理实现的灵感(特别是一种即使不需要也不会将IntegerRapper实例永久保留在内存中的实现)
请参见记忆结果!使用Scala,您甚至可以抽象出所有必需的逻辑 您需要的基础架构:
scala> :paste
// Entering paste mode (ctrl-D to finish)
// A little memoization utility
object Memoization extends Memoization
trait Memoization {
trait Memoizable[A] {
def memoize(fun: A): A
}
implicit def fun1memoizable[A, B] = new Memoizable[A => B] {
def memoize(f: A => B): (A => B) = new MemoizedFunction(f)
}
implicit def fun2memoizable[A, B, C] = new Memoizable[(A, B) => C] {
def memoize(f: (A, B) => C): (A, B) => C = {
val memo = new MemoizedFunction(f.tupled)
(a, b) => memo((a, b))
}
}
implicit def fun3memoizable[A, B, C, D] = new Memoizable[(A, B, C) => D] {
def memoize(f: (A, B, C) => D): (A, B, C) => D = {
val memo = new MemoizedFunction(f.tupled)
(a, b, c) => memo((a, b, c))
}
}
def memoize[F](f: F)(implicit m: Memoizable[F]): F = m memoize f
class MemoizedFunction[-A, +B](f: A => B) extends (A => B) {
private[this] val cache = collection.mutable.Map.empty[A, B]
def apply(a: A): B = cache.getOrElseUpdate(a, f(a))
}
}
import Memoization._
// Abstracting flyweight pattern
// (http://en.wikipedia.org/wiki/Flyweight_pattern)
trait Flyweight[Args, Obj] { self: (Args => Obj) =>
val on: Args => Obj = memoize(this : (Args => Obj))
}
// Ctrl+D
scala> :paste
// Entering paste mode (ctrl-D to finish)
case class IntegerWrapper private(i: Int) {
println(this.toString + " created.")
}
object IntegerWrapper extends (Int => IntegerWrapper)
with Flyweight[Int, IntegerWrapper]
// Ctrl+D
scala> IntegerWrapper.on(11)
IntegerWrapper(11) created.
res0: IntegerWrapper = IntegerWrapper(11)
scala> IntegerWrapper.on(11)
res1: IntegerWrapper = IntegerWrapper(11)
用法:
scala> :paste
// Entering paste mode (ctrl-D to finish)
// A little memoization utility
object Memoization extends Memoization
trait Memoization {
trait Memoizable[A] {
def memoize(fun: A): A
}
implicit def fun1memoizable[A, B] = new Memoizable[A => B] {
def memoize(f: A => B): (A => B) = new MemoizedFunction(f)
}
implicit def fun2memoizable[A, B, C] = new Memoizable[(A, B) => C] {
def memoize(f: (A, B) => C): (A, B) => C = {
val memo = new MemoizedFunction(f.tupled)
(a, b) => memo((a, b))
}
}
implicit def fun3memoizable[A, B, C, D] = new Memoizable[(A, B, C) => D] {
def memoize(f: (A, B, C) => D): (A, B, C) => D = {
val memo = new MemoizedFunction(f.tupled)
(a, b, c) => memo((a, b, c))
}
}
def memoize[F](f: F)(implicit m: Memoizable[F]): F = m memoize f
class MemoizedFunction[-A, +B](f: A => B) extends (A => B) {
private[this] val cache = collection.mutable.Map.empty[A, B]
def apply(a: A): B = cache.getOrElseUpdate(a, f(a))
}
}
import Memoization._
// Abstracting flyweight pattern
// (http://en.wikipedia.org/wiki/Flyweight_pattern)
trait Flyweight[Args, Obj] { self: (Args => Obj) =>
val on: Args => Obj = memoize(this : (Args => Obj))
}
// Ctrl+D
scala> :paste
// Entering paste mode (ctrl-D to finish)
case class IntegerWrapper private(i: Int) {
println(this.toString + " created.")
}
object IntegerWrapper extends (Int => IntegerWrapper)
with Flyweight[Int, IntegerWrapper]
// Ctrl+D
scala> IntegerWrapper.on(11)
IntegerWrapper(11) created.
res0: IntegerWrapper = IntegerWrapper(11)
scala> IntegerWrapper.on(11)
res1: IntegerWrapper = IntegerWrapper(11)
这是一个通用的解决方案,使用了
映射
。对于您的特定情况,您最好使用向量
。如果您只是试图避免分配,可能您想要的是一个?在Scala 2.10中,如果您的integerrapper
类扩展了AnyVal
,则实例通常不会得到分配,反之亦然将对值本身调用ead静态方法。例如:
scala> case class IntegerWrapper(val i: Int) extends AnyVal { def increment = i + 1 }
defined class IntegerWrapper
scala> object Test { IntegerWrapper(2).increment }
defined module Test
scala> :javap -verbose Test
...
public Test$();
Code:
Stack=2, Locals=1, Args_size=1
0: aload_0
1: invokespecial #13; //Method java/lang/Object."<init>":()V
4: aload_0
5: putstatic #15; //Field MODULE$:LTest$;
8: getstatic #20; //Field IntegerWrapper$.MODULE$:LIntegerWrapper$;
11: iconst_2
12: invokevirtual #24; //Method IntegerWrapper$.extension$increment:(I)I
15: pop
16: return
使用此版本,您可以看到对象分配和对新的
IntegerRapper
实例的方法的调用。存储在MemorizedFunction
中的缓存可能是collection.mutable.Map[a,WeakReference[B]
(或SoftReference[B]
)为了避免以防止对短期结果进行垃圾收集的方式缓存结果,映射将不幸地保留下来,但我想不出一种可靠的方法来使用适合这种通用解决方案的WeakHashMap。有必要定期/机会主义地清除映射。或者可以使用Google GuavaCacheBuilder
。我有一个基于案例类IntegerValue的代码库。因此,我正在寻找不破坏现有代码的东西。您可以很容易地调整此解决方案,使其不破坏现有代码。很高兴知道,所以我接受了您的答案。不幸的是,在我的案例中,IntegerValue扩展了一个公共特性RuntimeValue因此,扩展AnyVal对我来说不是一个选项。这很遗憾-我认为目前没有任何方法可以将trait与value类混合在一起。我能想到的唯一解决方法是编写一个从IntegerValue
到RuntimeValue
的隐式转换,以获得RuntimeValue
方法。然后当然你会得到>RuntimeValue
每次分配…你能解释为什么这比让IntegerRapper成为一个普通类并添加我自己的伴生对象、unapply、equals、hashcode等更好吗?@SilvioBierman确切地说,你不需要自己定义equals
、hashcode
等。好的,但这不排除Int中的模式匹配吗egerWrapper?@SilvioBierman您仍然需要定义未应用的
(请参阅我编辑的代码),但仅此而已。别忘了密封整合器
特性!很有趣。我将对此进行研究。