Bạn có thể định nghĩa một vài InternableN[Arg1, Arg2, ..., ResultType]
đặc điểm cho N là số đối số cho apply()
: Internable1[A,Z]
, Internable2[A,B,Z]
, v.v. Những đặc điểm này tự xác định bộ nhớ cache, phương pháp intern()
và phương pháp apply
mà chúng tôi muốn hijack.
Chúng tôi sẽ phải xác định một đặc điểm (hoặc một lớp trừu tượng) để đảm bảo các đặc điểm InternableN
của bạn rằng thực sự có một phương pháp áp dụng để được ghi đè lên, hãy gọi nó là Applyable
.
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: => 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) }
}
}
Đối tượng đồng hành của lớp học của bạn sẽ cần phải là một mixin của một lớp bê tông thực hiện ApplyableN
với InternableN
. Nó sẽ không hoạt động để áp dụng trực tiếp được xác định trong đối tượng đồng hành của bạn.
// 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]
Một điều tốt về việc áp dụng ban đầu này không cần phải sửa đổi để phục vụ cho thực tập. Nó chỉ tạo ra các cá thể và chỉ được gọi khi chúng cần được tạo ra.
Toàn bộ điều có thể (và nên) cũng được xác định cho các lớp có nhiều đối số. Đối với trường hợp hai đối số:
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: => 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]
Sự tương tác cho thấy Internables' áp dụng phương pháp thực hiện trước thời điểm ban đầu apply()
mà chỉ được thực hiện nếu cần thiết.
scala> import SomeClass._
import SomeClass._
scala> SomeClass(1)
Internable1: hijacking apply
original apply
res0: SomeClass = [email protected]
scala> import AnotherClass._
import AnotherClass._
scala> AnotherClass("earthling", "greetings")
Internable2: hijacking apply
original apply
res1: AnotherClass = [email protected]
scala> AnotherClass("earthling", "greetings")
Internable2: hijacking apply
res2: AnotherClass = [email protected]
Tôi chọn sử dụng WeakHashMap để bộ đệm ẩn không ngăn chặn việc thu gom rác của các cá thể thực tập khi chúng không còn được tham chiếu ở nơi khác.
Mã gọn gàng có sẵn as a Github gist.