Scala 宏 : Checking if a Java Field is marked as transient

标签 scala scala-macros

如何查看 ACC_TRANSIENT 使用 Scala 宏在 Java 类的字段上标记?

术语符号 具有 isPrivate 和 isProtected 之类的方法,但没有任何类型的 isTransient 方法。

检查 Scala 的 transient

对于 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的 transient ?

但是,如果我有一个 Java 类:
public class JavaExample {
  protected transient String ignoredField;
}

这会产生类似的字节码(该字段是 protected 而不是私有(private)的):
  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(...) 标记。但这对于使用编译时反射的宏来说似乎并不理想。我还没有弄清楚如何让它在宏中工作,所以我不确定它是否可能。

最佳答案

可能的解决方法/黑客解决方案

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

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 类,那么我认为您需要 CompileOrder.JavaThenScala在您的 build.sbt 中确保 Java 类在宏运行之前出现在类路径中:
compileOrder := CompileOrder.JavaThenScala

关于Scala 宏 : Checking if a Java Field is marked as transient,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24125409/

相关文章:

scala - 在 Scala 中定期运行一个函数

scala - 在 Spark 中使用前导窗口函数时是否可以忽略空值

scala - 使用 Intellij 自动导入的自定义 sbt 配置

scala - 在Scala中将方法提取到独立函数

scala - 使用Scala宏生成方法

scala - 将函数实现以编程方式将特定类型返回到另一个函数中

Scala,P08 - 包

scala - 如何让 Intellij 使用来自 SBT scala 的依赖项

Scala 宏 : Accessing members with quasiquotes

scala - 是否可以在 Scala 宏中从 WeakTypeTag 生成 Apply?