Scala宏:检查Java字段是否标记为瞬态

Scala宏:检查Java字段是否标记为瞬态,scala,scala-macros,Scala,Scala Macros,如何使用Scala宏检查Java类字段上的ACC\u TRANSIENT标志 TermSymbol具有类似isPrivate和isProtected的方法,但没有任何类型的isTransient方法 检查Scala的瞬态 对于Scala类,您使用@transient注释,该注释生成带有ACC_transient标志的字段: class ScalaExample { @transient protected var ignoredField: String = null } 在类文件中,您将

如何使用Scala宏检查Java类字段上的ACC\u TRANSIENT标志

TermSymbol具有类似isPrivate和isProtected的方法,但没有任何类型的isTransient方法

检查Scala的瞬态 对于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的瞬态? 但是,如果我有一个Java类:

public class JavaExample {
  protected transient String ignoredField;
}
产生类似字节码的字段(该字段受保护,而非私有):

没有注释:

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运行时反射,我可以获得java示例的java.lang.Class,然后使用java反射检查使用java.lang.reflect.Modifier.isTransient(…)的ACC_TRANSIENT标志。但对于使用编译时反射的宏来说,这似乎并不理想。我还没有弄明白如何让它在宏中工作,所以我不确定它是否可能。

可能的解决方法/黑客解决方案 我最终实施了问题中提到的可能的解决方法/黑客:

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

一个小提示:Java对Scala意义上的访问器一无所知,所以您不希望
.accessed
在其中。我不确定是否有办法读取Java字段是否在符号之外是瞬态的,但我不这么认为。这是一个编译器错误。我刚刚在问题跟踪系统中存档。谢谢你的解决办法。在Scala 2.12.4中,通过这个拉取请求似乎已经解决了这个问题:是的,最近已经解决了。
compileOrder := CompileOrder.JavaThenScala