8

我有class几个对象作为内部方法。

不久前我也问过这个问题并得到了一个很好的答案,但这会导致 servlet 容器中的致命错误。Scala 在请求类TypeTag时无法始终生成一个。URLClassLoader

有问题的项目是开源的,可在此处找到。

当前方法可在此处找到,但它不保留顺序。对象成员已正确初始化,但顺序随机。

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

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

更新

  • 不要根据此处的链接建议答案,它们已经过测试并且已知会失败。
  • 出于文体原因,使用 aval而不是不是一种选择。object
  • getMethodsgetDeclaredFields已知不保证订单。如果这是可能的,那么很可能是 Scala 反射。
4

4 回答 4

5

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

public Field[] getDeclaredFields() 抛出 SecurityException

返回一个 Field 对象数组,反映由此 Class 对象表示的类或接口声明的所有字段。这包括公共、受保护、默认(包)访问和私有字段,但不包括继承的字段。返回的数组中的元素没有排序,也没有任何特定的顺序。如果类或接口未声明任何字段,或者此 Class 对象表示原始类型、数组类或 void,则此方法返回长度为 0 的数组。请参阅 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)
  }
}

请参阅API 文档

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

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

我仍然认为通过将设计修改为与订单无关,可能通过对依赖项进行不同的编码来更好地为您服务。

于 2014-03-18T11:46:51.243 回答
0

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

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

尝试的解决方案:通过在构造/初始化阶段使用反射,以正确的顺序以编程方式预初始化这些对象。虽然如果 scala 或 java 反射与您一起工作,这将起作用,但它“违背常规” - 它与内部对象的定义背道而驰。如果您不希望它们被延迟初始化,那么为什么首先将它们声明为内部对象?

我建议的解决方案: 将声明从内部objects 更改为vals。根据所需的初始化顺序对这些声明进行排序。无需反射。

(旁白:在这个问题和相关问题中,您没有提到object必须使用 s 的人为约束或原因。但是,如果有一些奇怪的原因必须使用 object,那么您仍然可以避免反射。只需将每个对象的构造函数调用forceInit它所依赖的每个对象的方法。每个forceInit方法可以只返回 1。这会以正确的顺序触发延迟初始化。)

:-)

于 2014-03-20T06:36:48.987 回答
0

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

于 2014-03-14T13:22:16.500 回答
0

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

public @interface DefaultColumnPosition {
    int value();
}

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

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

于 2016-03-09T18:46:19.263 回答