5

假设定义:

case class IntegerWrapper(i : Int)

并且处于可能创建大量IntegerWrapper实例的情况下,i=[0..N>人们必须做什么:

  1. 将此范围映射到一组固定的单例[IntegerWrapper(0) .. IntegerWrapper(N)>

  2. 保留类的现有值语义IntegerWrapper(匹配、等于、哈希码、序列化)

我正在寻找类似于什么的实例共享java.lang.Integer。我想我的问题是是否可以在不必自己做所有事情的情况下做到这一点。简单地用 an 定义一个伴生对象是apply(i : Int)不会编译的。有什么建议么?

4

4 回答 4

6

如果您只是想避免分配,也许您想要的是一个值类?在 Scala 2.10 中,如果您的IntegerWrapper类 extends 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实例的方法的调用。

于 2012-11-05T13:18:58.203 回答
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对于您的特定情况,您可能会更好。

于 2012-11-05T13:14:51.593 回答
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

于 2012-11-05T13:01:53.253 回答
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请向客户端公开,或者恕我直言,更好地添加copyIntegerWrapper界面中。

模式匹配照常工作:

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
于 2012-11-05T12:58:02.333 回答