简体   繁体   中英

Scala Using WeakReference in A Case Class

I want to write a case class in scala that holds a scala.ref.WeakReference to some other object. I wonder what the best practice it is for that to be done in scala.

I had a few thoughts on that, and the first one was this:

case class CC1(ref: Any) {
    private val weakRef: WeakReference[Any] = WeakReference(ref)

    def get: Any = weakRef()
}

But that simply does not work because scala would automatically generate in CC1 a val for ref which holds a strong reference.

My second thought was:

case class CC2(private val weakRef: WeakReference[Any]) {
    def get: Any = weakRef() 
}

That worked fine. However, it is not very friendly in terms of code reuse -- what if the weakRef and the get come from a base class / trait like this:

trait T3 {
    protected var weakRef: WeakReference[Any] = null

    def get: Any = if(weakRef != null) weakRef() else null
}

case class CC3(/* how should I initialize weakRef in T3 */) extends T3 {
}

How would you write the code? What would you suggest, please? Thanks in advance!

What's wrong with this?

case class CC3(private val wr:WeakReference[AnyRef]) extends T3 {
  weakRef = wr  
}

If you want to avoid introducing another field, you can change the trait so that it does not contain a field but only the getter and setter methods.

trait T3 {
  protected def weakRef: WeakReference[AnyRef]
  protected def weakRef_=(wr:WeakReference[AnyRef])

  def get: Any = if(weakRef != null) weakRef() else null
}

Then you implement those methods in your case class.

case class CC3(private var wr:WeakReference[AnyRef]) extends T3 {
  override protected def weakRef = wr
  override protected def weakRef_=(w:WeakReference[AnyRef]) { wr = w }
}

I'm not sure why you want a weak reference in a case class, but presumably you want to be able to get your hands on the value, and otherwise want it to be transparent that it's actually weak--otherwise you can just put a WeakReference in the case class. First let's build a handy wrapper:

class Weakly[A](wr: java.lang.ref.WeakReference[A]) {
  def apply() = wr.get
  def get = Option(wr.get)
  def weak = wr
  override def hashCode = { val a = wr.get; if (a==null) wr.hashCode else a.hashCode }
  override def equals(a: Any) = a==wr.get
  override def toString = "~"+wr.get
}
object Weakly {
  def apply[A](a: A) = new Weakly(new java.lang.ref.WeakReference(a))
  def unapply[A](wa: Weakly[A]) = Option(wa())
}
implicit def strengthen_on_demand[A](w: Weakly[A]) = w()
implicit def weaken_on_demand[A](a: A) = Weakly(a)

Now our wrapped weak reference is wrapped twice, with the advantage that it displays its original hashCode and equals , that it displays the original string with ~ in front (you could change this of course), and most importantly that you can do pattern matching on the reference (which will only match if the reference is there). With this around, we now can make weak references in case classes with a minimum of extra fuss:

scala> case class CC1(s: Weakly[String]) {}
defined class CC1

scala> val cc = CC1("fish")
cc: CC1 = CC1(~fish)

scala> cc match { case CC1( Weakly(s) ) => s; case _ => "(nope)" }
res0: String = fish

scala> val s: String = cc.s
s: String = fish

scala> cc.s.weak.clear

scala> cc match { case CC1( Weakly(s) ) => s; case _ => "(nope)" }
res2: String = (nope)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM