我有一个界面:
trait MyInterface {
def doSomething(usefulName : Int) : Unit
}
我有一个宏来迭代接口的方法,并使用方法名称和参数。我通过这样的方式访问方法名称:
val tpe = typeOf[MyInterface]
// Get lists of parameter names for each method
val listOfParamLists = tpe.decls
.filter(_.isMethod)
.map(_.asMethod.paramLists.head.map(sym => sym.asTerm.name))
如果我打印出doSomething
参数的名称,则usefulName
已成为x$1
。为什么会发生这种情况并且有没有办法保留原始参数名称?
我使用scala版本2.11.8,macros paradise版本2.1.0和blackbox上下文。
接口实际上是我控制的单独sbt项目中的java源代码。我尝试过编译:
javacOptions in (Compile, compile) ++= Seq("-target", "1.8", "-source", "1.8", "-parameters")
参数标志应该保留名称,但我仍然得到与以前相同的结果。
答案 0 :(得分:4)
这与宏无关,与Scala的运行时反射系统有关。简而言之,Java 8和Scala 2.11都希望能够查找参数名称,并且每个都实现了它们的反射系统来完成它。
如果一切都是Scala并且你一起编译它就行了(呃!)。当你有一个必须单独编译的Java类时会出现问题。
首先要注意的是-parameters
标志仅在Java 8之后,与Scala 2.11差不多。因此Scala 2.11可能没有使用此功能来查找方法名称......请考虑以下内容
javac -parameters MyInterface.java
编译的MyInterface.java
public interface MyInterface {
public int doSomething(int bar);
}
scalac MyTrait.scala
编译的MyTrait.scala
class MyTrait {
def doSomething(bar: Int): Int
}
然后,我们可以使用MethodParameterSpy来检查Java 8 -parameter
标志应该给我们的参数信息名称。在Java编译接口上运行它,我们得到(这里我缩写了一些输出)
public abstract int MyInterface.doSomething(int)
参数名称:
bar
但是在Scala编译的类中,我们只得到
public abstract int MyTrait.doSomething(int)
参数名称:
arg0
然而,Scala查找自己的参数名称没有问题。这告诉我们Scala实际上根本不依赖于这个Java 8特性 - 它构建了自己的运行时系统来跟踪参数名称。然后,毫不奇怪,这对Java源代码的类不起作用。它生成名称x$1
,x$2
,...作为占位符,与Java 8反射生成名称arg0
,arg1
,...作为占位符的方式相同我们检查了编译的Scala特征。 (如果我们没有通过-parameters
,即使是MyInterface.java
也会生成这些名称。)
最好的解决方案(在2.11中工作)我可以想出获取Java类的参数名称是使用Scala的Java反射。像
这样的东西$ javac -parameters MyInterface.java
$ jar -cf MyInterface.jar MyInterface.class
$ scala -cp MyInterface.jar
scala> :pa
// Entering paste mode (ctrl-D to finish)
import java.lang.reflect._
Class.forName("MyInterface")
.getDeclaredMethods
.map(_.getParameters.map(_.getName))
// Exiting paste mode, now interpreting.
res: Array[Array[String]] = Array(Array(bar))
当然,只有拥有-parameter
标志(否则你得到arg0
),这才有效。
我还应该提一下,如果你不知道你的方法是用Java还是用Scala编译的,你可以随时调用.isJava
(例如:typeOf[MyInterface].decls.filter(_.isMethod).head.isJava
)然后分支无论是您的初始解决方案还是我上面提出的建议。
-parameter
编译的Java类,并且我的Java反射黑客也适用于Scala类。
一切都很好,结束了吗?