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 Guava
    CacheBuilder
    。我有一个基于案例类IntegerValue的代码库。因此,我正在寻找不破坏现有代码的东西。您可以很容易地调整此解决方案,使其不破坏现有代码。很高兴知道,所以我接受了您的答案。不幸的是,在我的案例中,IntegerValue扩展了一个公共特性RuntimeValue因此,扩展AnyVal对我来说不是一个选项。这很遗憾-我认为目前没有任何方法可以将trait与value类混合在一起。我能想到的唯一解决方法是编写一个从
    IntegerValue
    RuntimeValue
    的隐式转换,以获得
    RuntimeValue
    方法。然后当然你会得到
    >RuntimeValue
    每次分配…你能解释为什么这比让IntegerRapper成为一个普通类并添加我自己的伴生对象、unapply、equals、hashcode等更好吗?@SilvioBierman确切地说,你不需要自己定义
    equals
    hashcode
    等。好的,但这不排除Int中的模式匹配吗egerWrapper?@SilvioBierman您仍然需要定义
    未应用的
    (请参阅我编辑的代码),但仅此而已。别忘了密封
    整合器
    特性!很有趣。我将对此进行研究。