Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>You can define a few <code>InternableN[Arg1, Arg2, ..., ResultType]</code> traits for N being the number of arguments to <code>apply()</code>: <code>Internable1[A,Z]</code>, <code>Internable2[A,B,Z]</code>, etc. These traits define the cache itself, the <code>intern()</code> method and the <code>apply</code> method we want to <em>hijack</em>.</p> <p>We'll have to define a trait (or an abstract class) to assure your <code>InternableN</code> traits that there is indeed an apply method to be overriden, let's call it <code>Applyable</code>.</p> <pre><code>trait Applyable1[A, Z] { def apply(a: A): Z } trait Internable1[A, Z] extends Applyable1[A, Z] { private[this] val cache = WeakHashMap[(A), Z]() private[this] def intern(args: (A))(builder: =&gt; Z) = { cache.getOrElse(args, { val newObj = builder cache(args) = newObj newObj }) } abstract override def apply(arg: A) = { println("Internable1: hijacking apply") intern(arg) { super.apply(arg) } } } </code></pre> <p>The companion object of your class will have to be a mixin of a concrete class implementing <code>ApplyableN</code> with <code>InternableN</code>. It would not work to have apply directly defined in your companion object.</p> <pre><code>// class with one apply arg abstract class SomeClassCompanion extends Applyable1[Int, SomeClass] { def apply(value: Int): SomeClass = { println("original apply") new SomeClass(value) } } class SomeClass(val value: Int) object SomeClass extends SomeClassCompanion with Internable1[Int, SomeClass] </code></pre> <p>One good thing about this is that the original apply need not be modified to cater for interning. It only creates instances and is only called when they need to be created.</p> <p>The whole thing can (and should) also be defined for classes with more than one argument. For the two-argument case:</p> <pre><code>trait Applyable2[A, B, Z] { def apply(a: A, b: B): Z } trait Internable2[A, B, Z] extends Applyable2[A, B, Z] { private[this] val cache = WeakHashMap[(A, B), Z]() private[this] def intern(args: (A, B))(builder: =&gt; Z) = { cache.getOrElse(args, { val newObj = builder cache(args) = newObj newObj }) } abstract override def apply(a: A, b: B) = { println("Internable2: hijacking apply") intern((a, b)) { super.apply(a, b) } } } // class with two apply arg abstract class AnotherClassCompanion extends Applyable2[String, String, AnotherClass] { def apply(one: String, two: String): AnotherClass = { println("original apply") new AnotherClass(one, two) } } class AnotherClass(val one: String, val two: String) object AnotherClass extends AnotherClassCompanion with Internable2[String, String, AnotherClass] </code></pre> <p>The interaction shows that the Internables' apply method executes prior to the original <code>apply()</code> which gets executed only if needed.</p> <pre><code>scala&gt; import SomeClass._ import SomeClass._ scala&gt; SomeClass(1) Internable1: hijacking apply original apply res0: SomeClass = SomeClass@2e239525 scala&gt; import AnotherClass._ import AnotherClass._ scala&gt; AnotherClass("earthling", "greetings") Internable2: hijacking apply original apply res1: AnotherClass = AnotherClass@329b5c95 scala&gt; AnotherClass("earthling", "greetings") Internable2: hijacking apply res2: AnotherClass = AnotherClass@329b5c95 </code></pre> <p>I chose to use a WeakHashMap so that the interning cache does not prevent garbage collection of interned instances once they're no longer referenced elsewhere.</p> <p>Code neatly available <a href="https://gist.github.com/3757213" rel="nofollow">as a Github gist</a>.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload