如何使用Scala宏检查Java类字段上的 ACC_TRANSIENT 标志?
TermSymbol 包含 isPrivate 等方法。 isProtected 但没有任何 isTransient 方法。
对于Scala类,您可以使用 @transient 注释生成带有 ACC_TRANSIENT 标记的字段:
class ScalaExample {
@transient protected var ignoredField: String = null
}
在类文件中,您最终得到:
private transient java.lang.String ignoredField;
descriptor: Ljava/lang/String;
flags: ACC_PRIVATE, ACC_TRANSIENT
然后在宏中我可以看到@transient注释:
scala> typeOf[ScalaExample].member(TermName("ignoredField")).asTerm.accessed.annotations
res0: List[universe.Annotation] = List(scala.transient)
但是,如果我有一个Java类:
public class JavaExample {
protected transient String ignoredField;
}
产生类似的字节码(该字段受保护而非私有):
protected transient java.lang.String ignoredField;
descriptor: Ljava/lang/String;
flags: ACC_PROTECTED, ACC_TRANSIENT
没有注释:
scala> typeOf[JavaExample].member(TermName("ignoredField")).asTerm.accessed.annotations
java.lang.AssertionError: assertion failed: variable ignoredField
at scala.reflect.internal.Symbols$Symbol.accessed(Symbols.scala:1978)
at scala.reflect.internal.Symbols$Symbol.accessed(Symbols.scala:1974)
at scala.reflect.internal.Symbols$TermSymbol.accessed(Symbols.scala:2658)
... 43 elided
scala> typeOf[JavaExample].member(TermName("ignoredField")).asTerm.annotations
res2: List[universe.Annotation] = List()
我意识到,对于Scala类, @transient 注释可能正在从ScalaSignature而不是 ACC_TRANSIENT 标志中读取,这就是为什么它不显示对于java类。
使用Scala运行时反射我可以获取JavaExample的java.lang.Class,然后使用Java反射来检查 ACC_TRANSIENT 标志,使用java.lang.reflect.Modifier.isTransient(... )。但对于使用编译时间反射的宏来说,这似乎并不理想。我还没弄明白如何让它在宏中工作,所以我不确定它是否可能。
答案 0 :(得分:2)
我最终实现了问题中提到的可能的解决方法/ Hack:
import java.lang.reflect.Modifier
import java.net.URLClassLoader
import scala.reflect.macros._
abstract class MacroHelpers { self =>
val ctx: Context
import ctx.universe._
private lazy val classLoader: ClassLoader = new URLClassLoader(ctx.classPath.toArray)
def isJavaTransient(sym: Symbol): Boolean = {
if (!sym.isJava || !sym.isTerm || !sym.asTerm.isVar) return false
val className: String = sym.owner.asClass.fullName
val clazz: Class[_] = classLoader.loadClass(className)
Modifier.isTransient(clazz.getDeclaredField(sym.name.decoded).getModifiers())
}
}
对于我来说,使用Scala 2.10.4和2.11.1这似乎都很好。如果您在混合的Scala / Java项目中使用宏并且正在尝试检查项目中的Java类,那么我认为您需要在build.sbt中使用CompileOrder.JavaThenScala
以确保Java类出现在类路径中在宏运行之前:
compileOrder := CompileOrder.JavaThenScala