我有一个界面:
trait MyInterface {
def doSomething(usefulName : Int) : Unit
}
我有一个宏,它迭代接口(interface)的方法并使用方法名称和参数进行处理。我通过执行以下操作访问方法名称:
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、宏天堂版本 2.1.0 和黑盒上下文。
该接口(interface)实际上是我控制的一个单独的 sbt 项目中的 java 源代码。我试过编译:
javacOptions in (Compile, compile) ++= Seq("-target", "1.8", "-source", "1.8", "-parameters")
参数标志应该保留名称,但我仍然得到与以前相同的结果。
最佳答案
这与宏无关,与 Scala 的运行时反射系统有关。简而言之,Java 8 和 Scala 2.11 都希望能够查找参数名称,并且各自实现了自己的反射系统来做到这一点。
如果一切都是 Scala 并且你一起编译它(duh!),这很好用。当您有一个必须单独编译的 Java 类时,就会出现问题。
观察和问题
首先要注意的是 -parameters
flag 仅从 Java 8 开始,它大约与 Scala 2.11 一样古老。所以 Scala 2.11 可能没有使用这个特性来查找方法名......考虑以下
javac -parameters MyInterface.java
编译public interface MyInterface {
public int doSomething(int bar);
}
scalac MyTrait.scala
编译class MyTrait {
def doSomething(bar: Int): Int
}
然后,我们可以使用 MethodParameterSpy检查 Java 8
-parameter
的参数信息名称flag 应该给我们。在 Java 编译的界面上运行它,我们得到(这里我缩写了一些输出)
public abstract int MyInterface.doSomething(int)
Parameter name:
bar
但是在 Scala 编译的类中,我们只得到
public abstract int MyTrait.doSomething(int)
Parameter name:
arg0
然而,Scala 可以毫无问题地查找自己的参数名称。这告诉我们,Scala 实际上根本不依赖这个 Java 8 特性——它构建了自己的运行时系统来跟踪参数名称。然后,这对于来自 Java 源的类不起作用也就不足为奇了。它生成名称
x$1
, x$2
, ... 作为占位符,与 Java 8 反射生成名称 arg0
的方式相同, arg1
, ... 当我们检查编译的 Scala 特征时作为占位符。 (如果我们没有通过 -parameters
,它甚至会为 MyInterface.java
生成这些名称。)解决方案
我能想到的获取 Java 类的参数名称的最佳解决方案(适用于 2.11)是使用 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
)然后将其分支到您的初始解决方案或我上面提出的解决方案。future
幸运的是,这在 Scala 2.12 中已成为过去。如果我没看错 this ticket ,这意味着在 2.12 中,您的代码将适用于使用
-parameter
编译的 Java 类而且,我的 Java 反射技巧也适用于 Scala 类。一切都好,结局好?
关于scala - 在 scala 宏中保留方法参数名称,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38548167/