Scala Dynamic

Dynamic

Scala 2.10 开始,增加了Dynamic类型,所有直接或间接的继承自 Dynamic 的类,都可以实现。 
Dynamic 继承自 Any,它的源代码中什么都没有,只有一句话。

traitDynamic extends Any
  @meta.languageFeature("extension of type scala.Dynamic", enableRequired = true)
  sealed trait dynamics
  object dynamics extends dynamics

参考:http://www.scala-lang.org/api/current/scala/Dynamic.html

A marker trait that enables dynamic invocations. Instances x of this trait allow method invocations x.meth(args) for arbitrary method names meth and argument lists args as well as field accesses x.field for arbitrary field names field.

按照官方的描述,Dynamic 是一个运行动态执行的 trait 标识,它没有成员,具体的实现由编译器嵌入,它可以动态的执行任意名字的方法或参数。这个怎么理解呢,我姑且认为它是动态产生方法和字段吧。 
要想使用Dynamic ,需要打开编译器选项 -language:dynamics 或者 导入包:

    Scala 思考再三还是加入了 Dynamic Types,这个特性在 Scala 2.9 中是试验性的,必须用 -Xexperimental  进行开启,到了 Scala 2.10.0 中,只有代码中 import scala.language.dynamics 就可用了,或是编译时加 -language:dynamics 选项。

    虽然 Scala 2.10.0 加进了 Dynamic Types 特性,但 Scala 仍然是静态类型的语言,因为在编译器同样会检查多出来的类型。

    有了 Dynamic Types 之后,Scala 又可更 DSL 了,方法名的动态上可以让它随时包括深刻的业务含义。相比 Java 的 DSL 的能力就太逊了,我们几乎无法在 Java 面前提 DSL 这回事。

import scala.language.dynamics

Dynamic 的操作都会经过下面四个方法

  1. selectDynamic:创建字段或方法
  2. updateDynamic:更新字段或方法
  3. applyDynamic:执行方法,可以带参数
  4. applyDynamicNamed:执行方法,参数可以指定名称

同样是 apply 开头,所以这个方法是对 applyDynamic 方法的补充,即使没有 applyDynamicNamed,单用 applyDynamic 也能达成我们的要求。applyDynamicNamed 只是让你用命名参数调用时方便,也就是像

p.showInfo(screenName="Unmi", email="fantasia@sina.com")  这样用命名参数的方式来调用动态方法时会调用 updateDynamicNamed 方法。有了这个方法在命名传递参数就方便处理 key/value 值。

这四个方法在一个动态类中只能分别定义一个版本,否则会产生二义性,这和普通方法的重载不一样的。柯里化后的函数第二个括号中的参数可根据实际调用来定义,定义成  (args: Any*) 可包打天下。

代码示例

import scala.collection.mutable
import scala.language.dynamics
class DynamicPerson extends Dynamic {
  //定义一个方法类型 CallFun,它接收 Int 类型参数,并返回 String 类型 ,这个有点像 C# 中的 delegate 
  type CallFun = Int => String
  //Map对象,存放属性
  private val fields = mutable.HashMap.empty[String, Any].withDefault {
    key => throw new NoSuchFieldError(key)
  }
  //Map对象,存放方法对象
  private val functions = mutable.HashMap.empty[String, CallFun].withDefault {
    key => throw new NoSuchFieldError(key)
  }
  //选取对象
  def selectDynamic(key: String) = fields(key)
  /**
   * 更新key对应的value
   * 这里做了一个判断,如果key以call字符串开头,我们认为是args是CallFun类型
   */
  def updateDynamic(key: String)(args: Any): Unit = {
    args match {
      case func if key.startsWith("call") => functions(key) = func.asInstanceOf[CallFun]
      case _ => fields(key) = args
    }
  }
  //这个就是用来动态执行方法的
  def applyDynamic(key: String)(arg: Int) = {
    println(functions(key)(arg))
  }

  def applyDynamicNamed(method: String)(args: (String, Any)*) {
    println(s"applyDynamicNamed->$method called, args: $args")
    for ((key, value) <- args) {
      println(s"key: $key, value: $value")
    }
  }
}
object SimpleDemo {
  def main(args: Array[String]) {
    val person = new DynamicPerson()
    //设置Name属性
    person.Name = "Mike"
    //定义一个 call 方法
    person.callAge = (age: Int) => s"${person.Name} 今年 $age 岁"
    //通过 applyDynamic 执行 call 方法 
    person.callAge(80) //这句代码将打印出:"Mike 今年 80 岁"
    
    person.showInfo(screenName="Unmi", email="fantasia@sina.com")
  }
}

 

转载于:https://my.oschina.net/cloudcoder/blog/856394

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值