Java / Scala反射:按顺序获取类方法并强制对象初始化

时间:2014-03-14 13:03:05

标签: java scala reflection phantom-dsl

我有一个class,其中包含一些对象作为内部方法。

我前一段时间也问this question并得到了一个很好的答案,但这导致了servlet容器中的致命错误。当TypeTag要求上课时,Scala无法始终生成URLClassLoader

有问题的项目是开源的,找到here

找到当前方法here,但它不会保留顺序。对象成员已正确初始化,但是以随机顺序。

问题:如何收集班级成员:

  • 按照定义的顺序
  • 以线程安全的方式
  • 通过超级类型过滤它们
  • 和贪婪初始化对象(引用module.instance) ?

更新

  • 不建议根据这里的链接给出答案,它们已经过测试并且已知失败。
  • 出于文体原因,不能使用val代替object
  • getMethodsgetDeclaredFields已知不保证订单。如果这在某种程度上是可能的,那很可能是Scala的反思。

4 个答案:

答案 0 :(得分:5)

来自http://docs.oracle.com/javase/6/docs/api/java/lang/Class.html#getDeclaredFields()

  

public Field [] getDeclaredFields()                            抛出SecurityException

     

返回Field对象的数组,反映类或接口声明的所有字段   由此Class对象表示。这包括公共,受保护,默认(包)访问,以及   私有字段,但不包括继承的字段。 返回的数组中的元素未排序   没有任何特定的顺序。如果类或接口,此方法返回长度为0的数组   声明没有字段,或者此Class对象表示基本类型,数组类或void。      请参阅Java语言规范,第8.2和8.3节。

(我的重点)。 getDeclaredMethods()的文档中明确说明了类似的语言,但getDeclaredClasses()的文档中没有明确的类似语言(但可以认为IMO是隐含的)。

所以不,你不能依赖JVM上的Java反射排序;在实践中,我看到顺序因运行JVM的体系结构而异(32-与64位)。

如果你真的必须按特定顺序初始化对象(为什么?),你可以使用命名约定并手动排序;但是,将代码更改为与顺序无关的可能会更好。

更新

您似乎可以从Scala反射API中获取一些内容:

trait EarlyInit {
  val mirror = runtimeMirror(this.getClass.getClassLoader)
  val reflection  = mirror.reflect(this)

  mirror
    .classSymbol(this.getClass)
    .toType
    .members
    .sorted    /// This method is documented to return members "in declaration order"
    .filter(_.isModule)
    .foreach(m => reflection.reflectModule(m.asModule).instance)
  }
}

请参阅the API docs

  

对此范围中包含的符号进行排序,以便:1)符号以其所有者的线性化顺序显示。 2)具有相同所有者的符号以其声明的相同顺序出现。 3)合成成员(例如vals / vars的getter / setter)可能以任意顺序出现。

但是,这不能保证一般工作,特别是对于混合Java / Scala项目(因为实际上没有办法按声明顺序获取Java类的成员)。另外,请注意Scala运行时反射不是线程安全的,通常不被视为生产就绪。

我仍然觉得通过将设计修改为与顺序无关的方式可以更好地服务,可能是通过不同地编码依赖关系。

答案 1 :(得分:0)

AFAIK,这是不可能的。订单类成员的定义根本不会保存在.class文件中(至少使用Java反射)。

答案 2 :(得分:0)

你有一个内部有object个内容的课程。正如您所指出的,当其他一些代码引用/调用它们时,内部对象延迟初始化(类似于lazy val)。

问题:(a)上述延迟初始化PLUS(b)隐式对象定义之间的相互依赖关系。这意味着它们需要按照它们的依赖链的顺序进行初始化,但是它们没有明确地相互引用,因此正确排序的延迟初始化不会自动发生。

尝试解决方案:通过在构建/初始化阶段使用反射,以正确的顺序以编程方式预初始化这些对象。虽然如果scala或java反射与你合作,这将有效,但它反对谷物' - 它与内部对象的定义相反。如果你不想让它们进行惰性初始化,那么为什么要首先将它们声明为内部对象呢?

我建议的解决方案:将声明从内部object更改为val s。根据所需的初始化顺序对这些声明进行排序。不需要反思。

(旁白:在此Q&相关内容中,您已经提到了必须使用object的人为约束或原因。但是,如果有一些你必须使用对象的奇怪原因,然后你仍然可以避免反射。只需让每个对象的构造函数调用它所依赖的每个对象的方法forceInit。每个forceInit方法只能返回1这将以正确的顺序触发延迟初始化。)

: - )

答案 3 :(得分:0)

我遇到了类似的问题并使用了带有一些价值的注释来强制命令。特别是,我的对象模型驱动一个SWT表,所以我创建了:

public @interface DefaultColumnPosition {
    int value();
}

然后使用@DefaultColumnPosition(0)或任何数字注释字段。按值排序。有一点开销,如果有一个不同的方法,或者可能是强制订单但看起来不像它的类级别注释会更好。

没有尝试过Scala解决方案,但它看起来也很有趣,我可以试一试。