如何实现案例类的实例共享

时间:2012-11-05 12:45:10

标签: class scala case-class

假设明确:

case class IntegerWrapper(i : Int)

并且处于可能会创建IntegerWrapperi=[0..N>[IntegerWrapper(0) .. IntegerWrapper(N)>个实例的情况下,需要做什么:

  1. 将此范围映射到一组固定的单例IntegerWrapper

  2. 保留类java.lang.Integer的现有值语义(匹配,等号,哈希码,序列化)

  3. 我希望实例分享类似apply(i : Int)所做的事情。我想我的问题是,如果可以在不必自己做所有事情的情况下完成。简单地使用{{1}}定义伴随对象不会编译。有什么建议吗?

4 个答案:

答案 0 :(得分:6)

如果你只是想避免分配,也许你想要的是value class?在Scala 2.10中,如果IntegerWrapper类扩展AnyVal,实例通常不会被分配,而是在值本身上调用静态方法。例如:

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

请注意,在那里调用的扩展方法是Int => Int

为了进行比较,如果你不扩展AnyVal,这就是你得到的:

scala> :javap -verbose Test
...
public Test$();
  Code:
   Stack=3, 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:   new #17; //class IntegerWrapper
   11:  dup
   12:  iconst_2
   13:  invokespecial   #20; //Method IntegerWrapper."<init>":(I)V
   16:  invokevirtual   #24; //Method IntegerWrapper.increment:()I
   19:  pop
   20:  return

使用此版本,您可以看到对象分配以及对新IntegerWrapper实例的方法的调用。

答案 1 :(得分:4)

记住结果!使用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)

这是一般解决方案,使用Map。对于您的特定情况,您可能最好使用Vector

答案 2 :(得分:3)

这与scala的Symbol类基本相同。因此,您可以将Symbol.scala视为合理实现的灵感(特别是,即使不需要,也不会将IntegerWrapper实例永久保留在内存中)。

请参阅https://lampsvn.epfl.ch/trac/scala/browser/scala/tags/R_2_9_1_final/src//library/scala/Symbol.scala#L1

答案 3 :(得分:2)

这样的东西?

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)
}

优点是编译器仍然生成equalshashCode,因为IntegerWrapperImpl是一个案例类。缺点是您不能直接使用其他编译器添加的案例类好东西,例如copy。如果您想使用它,请将IntegerWrapperImpl公开给客户端,或者更好地将IMHO添加到copy界面。

模式匹配照常工作:

IntegerWrapper