Scala 反射可以提取特征中使用的初始值吗?

Scala 反射可以提取特征中使用的初始值吗?,scala,reflection,scala-2.10,Scala,Reflection,Scala 2.10,我在玩弄反思,以便对一种特质进行深入分析。我希望得到的一个结果是为成员字段设置的初始值。例如,在trait中: trait A { val x: Int = 3 val y: String = "y" } 知道3和“y”会很好。由于以下输出(由scalac-Xprint生成),我在API中未找到任何与此任务相关的内容: 抽象特征扩展对象{ def com$hablaps$A$_setter_u$x(x$1:Int):单位; def com$hablaps$A$_setter_u$y=(

我在玩弄反思,以便对一种特质进行深入分析。我希望得到的一个结果是为成员字段设置的初始值。例如,在trait中:

trait A {
  val x: Int = 3
  val y: String = "y"
}
知道3和“y”会很好。由于以下输出(由scalac-Xprint生成),我在API中未找到任何与此任务相关的内容:

抽象特征扩展对象{
def com$hablaps$A$_setter_u$x(x$1:Int):单位;
def com$hablaps$A$_setter_u$y=(x$1:String):单位;
def x():Int;
def y():字符串
};
$class扩展的抽象特性{
def/*A$class*/$init$($this:com.hablaps.A):单位={
$this.com$hablaps$A$\u setter\$x\=(3);
$this.com$hablaps$A$\u setter\u$y=(“y”);
()
}
}

我担心访问它们会非常困难,因为它们保存在$init$方法的主体中。有什么(简单的)方法可以通过反射获得这些值吗?

我怀疑你能反思到这一点。这不是关于类型的信息,而是代码。如果你有这棵树的特质,你可以找到它,但是,否则,我怀疑它

但是,您可以使用类文件解析器来进一步研究这个问题。我假设这些将显示为类的常量,可以读取。我不确定你能不能把它们和变量联系起来,但是


我不太熟悉类文件解析器,但我认为一个名为“asm”的工具可以做到这一点。

您必须反汇编字节码:

trait A { val x: Int = 3 }

public abstract class A$class extends java.lang.Object{
public static void $init$(A);
  Code:
   0:    aload_0
   1:    iconst_3
   2:    invokeinterface #12,  2; //InterfaceMethod A.A$_setter_$x_$eq:(I)V
   7:    return
请参见第1行——该值唯一存在的位置是init方法的字节码

你不能以任何其他方式达到这个目的,因为如果你有

trait A { val x: Int = 3 }
trait B extends A { override val x = 7 }
class C extends B {}
您发现
C
A$\u setter\u$x\u$eq
扩展为什么都不做——使
成为一个$class。$init$
调用一个no op并使该值不可恢复

证明:

public class C extends java.lang.Object implements B,scala.ScalaObject{
public void A$_setter_$x_$eq(int);
  Code:
   0:    return

public void B$_setter_$x_$eq(int);
  Code:
   0:    aload_0
   1:    iload_1
   2:    putfield #11; //Field x:I
   5:    return

您可以使用Java的代理来创建trait的实例,该实例从对trait的…$setter$的所有调用中收集值。。。方法。下面是一个骇人的例子:

object Reflection {
  def traitInits(clazz : Class[_]) : Map[String, Object] = {
    var cl = clazz.getClassLoader
    if (cl == null) {
      cl = ClassLoader.getSystemClassLoader
    }

    var init : Option[java.lang.reflect.Method] = None
    try {
      for (m <- cl.loadClass(clazz.getName + "$class").getMethods if init.isEmpty)
        if (m.getName == "$init$")
          init = Some(m)
    } catch {
      case e : Exception =>
    }

    if (init.isEmpty) return Map()

    var encodedToDecodedSetterNameMap = Map[String, String]()

    for (m <- clazz.getDeclaredMethods()) {
      val setterPrefix = clazz.getName.replace('.', '$') + "$_setter_$"
      val encoded = m.getName
      if (encoded.startsWith(setterPrefix)) {
        val decoded = encoded.substring(setterPrefix.length, encoded.length - 4)
        encodedToDecodedSetterNameMap += (encoded -> decoded)
      }
    }

    var result = Map[String, Object]()

    import java.lang.reflect.InvocationHandler
    import java.lang.reflect.Proxy

    init.get.invoke(null, Proxy.newProxyInstance(cl, Array[Class[_]](clazz),
      new InvocationHandler {
        def invoke(proxy : Object,
                   method : java.lang.reflect.Method,
                   args : Array[Object]) = {
          encodedToDecodedSetterNameMap.get(method.getName) match {
            case Some(decodedName) => result += (decodedName -> args(0))
            case _                 =>
          }
          null
        }
      }))

    result
  }                                               //> traitInits: (clazz: Class[_])Map[String,Object]

  trait A {
    val x : Int = 3
    val y : String = "y"
  }

  traitInits(classOf[A])                          //> res0: Map[String,Object] = Map(x -> 3, y -> y)
}
对象反射{
def traitInits(clazz:Class[]):Map[String,Object]={
var cl=clazz.getClassLoader
如果(cl==null){
cl=ClassLoader.getSystemClassLoader
}
var init:Option[java.lang.reflect.Method]=None
试一试{
对于(m)
}
if(init.isEmpty)返回映射()
var encodedCodedSetterNameap=Map[String,String]()
用于(m解码)
}
}
var result=Map[字符串,对象]()
导入java.lang.reflect.InvocationHandler
导入java.lang.reflect.Proxy
init.get.invoke(null,Proxy.newProxyInstance(cl,数组[Class[]](clazz)),
新调用处理程序{
def调用(代理:对象,
方法:java.lang.reflect.method,
args:Array[Object])={
encodedCodedSetterNameap.get(method.getName)匹配{
案例部分(decodedName)=>result+=(decodedName->args(0))
案例=>
}
无效的
}
}))
结果
}//>traitInits:(clazz:Class[])Map[字符串,对象]
特征A{
val x:Int=3
val y:String=“y”
}
traitInits(classOf[A])/>res0:Map[String,Object]=Map(x->3,y->y)
}

我忘了说我一直在关注这一点,这对于从trait中提取大量信息非常有用。我不熟悉Scala反射,但是如果您能够执行
新的{}.x
,那么你应该没问题。是的,实例化并获取值,不管是否通过反射。希望没有任何副作用。是的,在这种情况下它肯定会工作,但我隐藏了一些细节:反射在一个函数中工作,该函数接收特性作为类型参数(
analyze[t]
)而且trait不一定要带有初始值。因此,生成实例可能会变得非常复杂,我一直在寻找更干净的方法。无论如何,我到目前为止还没有考虑过这个简单的解决方案,如果其他任何方法都失败了,考虑它可能会很有趣。谢谢!是的,我得出了结论我认为拥有树会很好,但是有没有办法从TypeTag中获得它呢?无论如何,我会看看ASM。谢谢,Daniel。是的,
C
缺少3,但它仍然可以使用重写7,它将成为
C
的初始值。因此,在分析这个类的情况下,我不关心
a
初始值都不是。相反,我想知道7。无论如何,这是非常有趣的东西。你是如何生成输出的?@JesúsLópez这是用
javap
完成的。REPL上甚至有一个
:javap
命令,但它需要JDK中的
tools.jar
位于类路径上,并且它只适用于预编译的stuff、 不是为了你在REPL本身上定义的东西。@JesúsLópez-你说的“初始值”是什么意思?它是一个val。它不能更改。如果
C
直接覆盖
a
,这与
B
的情况是一样的——该值很可能只存在于字节码中,并且不可访问,因为
a$\u setter\u$x\u$eq
被覆盖。@DanielC.Sobral-
:javap
将对您在REPL中定义的内容起作用;它只是一点点我对此很挑剔,因为它优先于给定名称的第一件事(不是范围中的当前名称),不能访问辅助类,并且还有一些其他的怪癖。@RexKerr,我从来没有让它工作过。它给了我
找不到“myclass”的类字节。
object Reflection {
  def traitInits(clazz : Class[_]) : Map[String, Object] = {
    var cl = clazz.getClassLoader
    if (cl == null) {
      cl = ClassLoader.getSystemClassLoader
    }

    var init : Option[java.lang.reflect.Method] = None
    try {
      for (m <- cl.loadClass(clazz.getName + "$class").getMethods if init.isEmpty)
        if (m.getName == "$init$")
          init = Some(m)
    } catch {
      case e : Exception =>
    }

    if (init.isEmpty) return Map()

    var encodedToDecodedSetterNameMap = Map[String, String]()

    for (m <- clazz.getDeclaredMethods()) {
      val setterPrefix = clazz.getName.replace('.', '$') + "$_setter_$"
      val encoded = m.getName
      if (encoded.startsWith(setterPrefix)) {
        val decoded = encoded.substring(setterPrefix.length, encoded.length - 4)
        encodedToDecodedSetterNameMap += (encoded -> decoded)
      }
    }

    var result = Map[String, Object]()

    import java.lang.reflect.InvocationHandler
    import java.lang.reflect.Proxy

    init.get.invoke(null, Proxy.newProxyInstance(cl, Array[Class[_]](clazz),
      new InvocationHandler {
        def invoke(proxy : Object,
                   method : java.lang.reflect.Method,
                   args : Array[Object]) = {
          encodedToDecodedSetterNameMap.get(method.getName) match {
            case Some(decodedName) => result += (decodedName -> args(0))
            case _                 =>
          }
          null
        }
      }))

    result
  }                                               //> traitInits: (clazz: Class[_])Map[String,Object]

  trait A {
    val x : Int = 3
    val y : String = "y"
  }

  traitInits(classOf[A])                          //> res0: Map[String,Object] = Map(x -> 3, y -> y)
}