Freewind @ Thoughtworks scala java javascript dart 工具 编程实践 月结 math python english [comments admin] [feed]

(2011-09-13) 在scala中,如何取得一个函数的参数名

广告: 云梯:翻墙vpn (省10元) 土行孙:科研用户翻墙http proxy (有优惠)

问题很简单,定义了一个函数,怎样才能取得参数名?

class User {
    def insert(name:String, age: Int) {}
 }

在scala中,有没有办法得到这个函数的参数名name和age呢?


因为scala在很多地方受制于java,先让我们看看在java中怎么解决。java的反射,可以得到类名,函数名,参数类型,就是得不到参数的名。曾经在jdk6.0时打算加入,后来因为时间不足放弃,而现在java7.0也没打算加入,可能是考虑到代码的兼容。在java中,不外乎是两种做法:

  1. 在编译时,使用一些(ant或mvn的)插件,扫描源代码,将这些信息加入到生成的字节码中,然后再用一些库读取。
  2. 在编译时,打开编译器的-g开关,让生成的字节码中包含有参数名等信息,再用库读取。

Paranamer这个库就是按第2种方式做的,见:http://paranamer.codehaus.org/

在scala中,按说也可以这么做。但是我尝试了几次,却都没有成功。scalac的-g参数,后面还有5个可选值,分别是none,source,vars,line,notailcall,我都试了,可是使用paranamer,都什么也没得到,看来scala的编译器跟java的做法还不一样。是不是就没有办法做到了?

最后,在stackoverflow上,有一个高手给我介绍了他写的一个工具,叫scalaj-reflect,可以做到。见:

http://stackoverflow.com/questions/5191726/is-it-possible-to-get-the-parameter-names-of-a-method-in-scala

通过这一段代码:

import scalaj.reflect._
for {
  clazz <- Mirror.ofClass[UsersController].toSeq
  method <- clazz.allDefs.find(_.name == "insert").toSeq
  params <- method.flatParams
} yield params

就可以成功得到insert函数的参数值了。

其项目位于:https://github.com/scalaj/scalaj-reflect

需要手动编译,得到jar。

这么高难的,scala都能解决。

经过后来在实际项目中使用,发现如果一个类的结构比较复杂,比如函数中有内部类或者闭包,可能读取错误。所以如果要使用这个功能,一定要注意测试。

comments powered by Disqus