Scala 2.10.0 新特性之动态属性、方法

最早的介绍 Scala 语言的书都是以 Scala 的静态类型系统为傲。Scala 也算是个脚本语言,却不像其他许多脚本语言那样类型是动态的,只有执行时才确定,而 Scala 在执行前就确定了类型,比如依赖于比 Java 更强大的类型推断行为。

静态类型不光是变量类型是确定的,还有比如在使用  qual.sel 时,sel 这个属性或是方法(Scala 的访问一致性,属性和方法有时候并没有那么大的区别)必须在 qual 的类型中声明了的。

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 这回事。

通俗点讲动态类型的类必须继承自 Dynamic,当使用 qual.sel,而 Qual 类未定义 sel 属性或方法时; 会调用 selectDynamic(method: String) 方法,当 qual.name = "Unmi" 时会调用类似 updateDynamic(method: String)(args: Any) 这样的方法; 还有 applyDynamic,applyDynamicNamed 这两个方法的自动调用。

所有的变化就在下面这四个方法中:

selectDynamic
updateDynamic
applyDynamic
applyDynamicNamed

看个完整的例子,我不打算把上面四个方法的应用规则分开来演示:

上面对 p 的每一个调用都说明了会委派给哪个动态方法,执行结果输出是:

现在来看发生了什么,Person 继承自  Dynamic,并且有引入 scala.language.dynamics。对 p 调用的方法(属性) 都不存在,但是都调用到了正常的动态方法。所以仍然要对这四个动态方法(确切的讲是四种类型的方法,因为比如你可以定义多个不同的 updateDynamic 方法,其余三个也同此) 分别加以说明:

1. selectDynamic

在调用找不到了无参方法时,会去寻找它,调用效果如下:

p.sayHello  也可以写成  p.selectDynamic("sayHello")

也就是说编译器在看到 p.sayHello 调用会根据  selectDynamic(method: String) 相当于创建了方法 def sayHello = .......,也就是把动态方法 selectDynamic(method: String) 换成   sayHello 却是。所以说 Scala 的 Dynamic 类中的 xxxDynamic 方法相当是模板方法。

applyDynamic,updateDynamic 和  applyDynamicNamed 这三个方法第二个括号中的参数类型,或个数需根据实际应用来定。这四个动态方法的第一个括号中的参数都是动态调用时的方法名。

2. applyDynamic

在进行有参数的方法调用时,会去找寻它,调用效果如下:

p.config("Hello", "Unmi") 可以写成 p.applyDynamic("config")("Hello", "Unmi")

还是这么理解: 把这个动态方法定义的方法名和第一个括号与参数替换成调用的方法名就知道怎么回事,例如把:

def applyDynamic(method: String)(args: Any*) 中的 applyDynamic(method: String) 替换成被调用方法名  config,就是:

def config(args: Any*)        //p.config("Hello", "Unmi") 要调用的就是这么个方法

所以第二个括号中的参数由你自己来定,比如说想这么调用 p.config("Hello", 100, 30),那么你可的动态方法可以这么定义:

def applyDynamic(method: String)(greeting: String, high: Int, low: Int) { ...... }

这个规则同样适用于 updateDynamic 和  applyDynamicNamed 这两个方法。

3. updateDynamic

等号赋值操作时会调用 updateDynamic 方法,调用效果如下:

p.products = ("iPhone", "Nexus")  可写成 p.updateDynamic("products")(("iPhone", "Nexus")),按照同样的理解方法,相当于 Person 中定义了 def products(args: Any) 方法。

4. applyDynamicNamed

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

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

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

参考: 1. SIP 17: Type Dynamic
          2. http://www.scala-lang.org/api/current/index.html#scala.Dynamic
          3. Dynamic Types in Scala 2.10
          4. Scala 2.10: class OhMy extends Dynamic !
          5. dynamic semantics

本文链接 https://yanbin.blog/scala-2-10-0-new-feature-dynamic/, 来自 隔叶黄莺 Yanbin Blog

[版权声明] Creative Commons License 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。

Subscribe
Notify of
guest

2 Comments
Inline Feedbacks
View all comments
浮生
浮生
10 years ago

楼主写的关于与scala的文章,写的非常好,对于学习scala编程有很大的帮助。就是感觉scala的文章太少了,希望楼主能多更新些文章,也多写些心得与大家分享。最后个人点请求,楼主能不能写一篇介绍scala资源文件(scala doc)各个包的大概的作用。因为文档都是英文的,有时候看了英文解释也不知道它具体的作用。