我知道我可以像这样添加动态“字段”:
import collection.mutable
class DynamicType extends Dynamic {
private val fields = mutable.Map.empty[String, Any].withDefault {key => throw new NoSuchFieldError(key)}
def selectDynamic(key: String) = fields(key)
def updateDynamic(key: String)(value: Any) = fields(key) = value
def applyDynamic(key: String)(args: Any*) = fields(key)
}
然后我可以做这样的事情:
val foo = new DynamicType
foo.age = 23
foo.name = "Rick"
但是,我想进一步扩展这一步骤并添加动态方法,例如:
foo.greet = (name: String) => s"Nice to meet you $name, my name is ${this.name}"
foo.greet("Nat"); //should return "Nice to meet you Nat, my name is Rick"
我尝试将所有方法存储在单独的地图中,updateDynamic
但我无法找到一种通用的方法来处理arity 问题。那么有没有办法使用 Macros + Dynamics 来获得这样的东西?
编辑:基于@Petr Pudlak 的回答,我尝试实现这样的事情:
import collection.mutable
import DynamicType._
/**
* An useful dynamic type that let's you add/delete fields and methods during runtime to a structure
*/
class DynamicType extends Dynamic {
private val fields = mutable.Map.empty[String, Any] withDefault { key => throw new NoSuchFieldError(key) }
private val methods = mutable.Map.empty[String, GenFn] withDefault { key => throw new NoSuchMethodError(key) }
def selectDynamic(key: String) = fields(key)
def updateDynamic(key: String)(value: Any) = value match {
case fn0: Function0[Any] => methods(key) = {case Seq() => fn0()}
case fn1: Function1[Any, Any] => methods(key) = fn1
case fn2: Function2[Any, Any, Any] => methods(key) = fn2
case _ => fields(key) = value
}
def applyDynamic(key: String)(args: Any*) = methods(key)(args)
/**
* Deletes a field (methods are fields too)
* @return the old field value
*/
def delete(key: String) = fields.remove(key)
//todo: export/print to json
}
object DynamicType {
import reflect.ClassTag
type GenFn = PartialFunction[Seq[Any],Any]
implicit def toGenFn1[A: ClassTag](f: (A) => Any): GenFn = { case Seq(a: A) => f(a) }
implicit def toGenFn2[A: ClassTag, B: ClassTag](f: (A, B) => Any): GenFn = { case Seq(a: A, b: B) => f(a, b) }
// todo: generalize to 22-args
}
1)它正确处理字段与方法(甚至是 0-args),但非常冗长(目前最多只能使用 2 个 arg 方法)。有没有办法简化我的代码?
2)无论如何支持动态方法重载(例如添加2个具有不同签名的动态方法?)如果我可以获得函数的签名,我可以将它用作我的methods
地图中的键。