使用Scala反射查找封闭实例(如果有)

4

我正在尝试使用Scala反射来定义traits和case classes及其伴生对象,以便实现“可导出性”(例如到Map [String,Any])并从中进行“可导入性”。对于顶层case class,它运行得很好,但我无法使内部类起作用。如果我已经有了一个关于封闭实例的处理,我就知道如何通过反射来实例化内部类,这个处理可以反映到InstanceMirror上,但现在我正在编写一个稍后由顶级或内部类实现的trait。

只要伴生对象和构造实例共享相同的封闭实例,我应该能够使其工作。但是我一直没有弄清楚如何通过反射来确定伴生对象的封闭实例。

以下是我正在尝试做的事情的简化示例以及出现的问题:

import scala.reflect.runtime.universe._;

trait CompanionOfReflectiveConstructable[T] {
  def tType : Type;

  lazy val mirror       : Mirror       = runtimeMirror( this.getClass.getClassLoader );
  lazy val ctorDecl     : Symbol       = tType.declaration(nme.CONSTRUCTOR);
  lazy val ctor         : MethodSymbol = ctorDecl.asMethod;
  lazy val tClass       : ClassSymbol  = tType.typeSymbol.asClass;
  lazy val tClassMirror : ClassMirror  = mirror.reflectClass( tClass );
  lazy val ctorF        : MethodMirror = tClassMirror.reflectConstructor( ctor );

  // in real-life, i'd derive arguments from ctor.paramss
  // but to highlight our issue, we'll assume a no arg constructor
  def createInstance : T = ctorF().asInstanceOf[T]; 
}

trait ReflectiveConstructable;

object Test1 extends CompanionOfReflectiveConstructable[Test1] {
  def tType = typeOf[Test1];
}
class Test1 extends ReflectiveConstructable;

class Outer {
  object Test2 extends CompanionOfReflectiveConstructable[Test2] {
    def tType = typeOf[Test2];
  }
  class Test2 extends ReflectiveConstructable;
}

这是发生的情况。
scala> Test1.createInstance
res0: Test1 = Test1@2b52833d

scala> (new Outer).Test2.createInstance
scala.ScalaReflectionException: class Test2 is an inner class, use reflectClass on an InstanceMirror to obtain its ClassMirror
        at scala.reflect.runtime.JavaMirrors$JavaMirror.ErrorInnerClass(JavaMirrors.scala:126)
...

Test1的表现很好。 Test2的问题很明显 - 我需要通过InstanceMirror而不是通过顶级mirror获取我的ClassMirror。(请参见这里这里这里)。但是,在CompanionOfReflectiveConstructable中,我不知道如何检查自己是否为内部对象或者我的封闭实例是谁,以有条件地执行适当的工作。有人知道如何做到这一点吗?

非常感谢!

1个回答

1
我看到的主要问题是您在内部类的伴生对象中使用了反射。Test2类将定义一个名为$outer的字段,其中包含外部类,但是从Test2伴生对象中,您无法获取所需创建Test2的Outer实例。我唯一能想到的解决方法是向您的伴生特性传递可选实例:
trait CompanionOfReflectiveConstructable[T] {
  def tType: Type

  def outer: Option[AnyRef] = None

  val mirror = runtimeMirror(this.getClass.getClassLoader)
  val tClassMirror = outer
    .map(a => mirror.reflect(a).reflectClass(tType.typeSymbol.asClass))
    .getOrElse(mirror.reflectClass(tType.typeSymbol.asClass))

  val ctorF = tClassMirror.reflectConstructor(tType.declaration(nme.CONSTRUCTOR).asMethod)

  // in real-life, i'd derive arguments from ctor.paramss
  // but to highlight our issue, we'll assume a no arg constructor
  def createInstance: T = ctorF().asInstanceOf[T]
}

trait ReflectiveConstructable

class Test1 extends ReflectiveConstructable

object Test1 extends CompanionOfReflectiveConstructable[Test1] {
  def tType = typeOf[Test1]
}

class Outer {

  object Test2 extends CompanionOfReflectiveConstructable[Test2] {
    def tType = typeOf[Test2]
    override def outer = Option(Outer.this)
  }

  class Test2 extends ReflectiveConstructable
}

如果能够从Test2伴生对象中获取Outer实例就好了,但可悲的是在运行时它不可用。


所以这是一个不错的解决方法,可能是最好的方法。但它并不是一种解决方案,也不是一种反射性地查找封闭实例的手段 :( 在我看来,Scala 反射似乎无法正确处理路径相关类型。在我的示例中,对于 Test2 的实例,ctor.owner.owner 应该以某种方式表示一个实例,但实际上是一个类。看起来反射可以看到 Outer#Test2 类型,但无法看到 o.Test2 类型,其中 o 是 outer 的一个实例。 - Steve Waldman
因此,当编译内部伴生对象时,它不会像内部类一样获取对外部类的引用(它是默认构造函数的一部分)。如果您查看Outer实例,则具有对模块(伴生对象)的volatile引用,但模块没有对外部类的引用。因此,现在您的trait无法获取对外部类的引用,因为它应用于内部伴生对象。您可能可以使用Scala宏来解决此问题,否则我认为无法通过任何反射方式获取外部类实例引用。 - Noah
所以,我还没有尝试跟随字节码,但内部模块不引用封闭实例是不可能绝对成立的吧?如果您引用封闭实例成员会怎样呢?那么就必须有一条返回路径。无论如何,这意味着反射不能为内部模块和路径相关类型做真正应该做的事情。(很抱歉我不能接受答案;我希望您能获得最高评分的一半奖金。但问题仍未解决,也许是因为它是无法解决的。) - Steve Waldman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接