简体   繁体   English

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

[英]Java/Scala reflection: Get class methods in order and force object init

I have a class with a few objects as inner methods. 我有一个带有一些对象的class作为内部方法。

I also asked this question a while ago and got a good answer, but that leads to fatal errors in servlet containers. 不久前我也问了这个问题 ,并得到了很好的答案,但这会导致Servlet容器中出现致命错误。 Scala cannot consistently generate a TypeTag when the URLClassLoader asks for a class. URLClassLoader请求一个类时,Scala无法一致地生成TypeTag

The project in question is open-source, found here . 有问题的项目是开源的,可以在此处找到。

The current approach is found here , but it doesn't preserve order. 这里可以找到当前的方法,但是它不能保留顺序。 object members are correctly initialised, but in a random order. 对象成员已正确初始化,但顺序随机。

Question : How can you collect class members: 问题 :如何收集班级成员:

  • in the order they are defined 按照定义的顺序
  • in a thread-safe way 以线程安全的方式
  • filter them by a super type 用超级类型过滤
  • and greedy initliase the objects(referencing module.instance ) ? 并贪婪初始化对象(引用module.instance )?

Update : 更新

  • Don't suggest answers based on the links here, they've been tested and known to fail. 不要根据此处的链接建议答案,因为它们已经过测试,并且已知会失败。
  • Using a val instead of object is not an option, for stylistic reasons. 出于风格原因,不能选择使用val而不是object
  • getMethods or getDeclaredFields is known not to guarantee order. 已知getMethodsgetDeclaredFields不保证顺序。 If this is somehow possible, it's likely with Scala reflection. 如果以某种方式可行,则可能是Scala反射。

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

public Field[] getDeclaredFields() throws SecurityException public Field [] getDeclaredFields()引发SecurityException

Returns an array of Field objects reflecting all the fields declared by the class or interface represented by this Class object. 返回一个Field对象数组,该数组反映由该Class对象表示的类或接口声明的所有字段。 This includes public, protected, default (package) access, and private fields, but excludes inherited fields. 这包括公共,受保护,默认(程序包)访问和私有字段,但不包括继承的字段。 The elements in the array returned are not sorted and are not in any particular order. 返回的数组中的元素未排序,并且没有任何特定顺序。 This method returns an array of length 0 if the class or interface declares no fields, or if this Class object represents a primitive type, an array class, or void. 如果类或接口未声明任何字段,或者此Class对象表示原始类型,数组类或void,则此方法返回长度为0的数组。 See The Java Language Specification, sections 8.2 and 8.3. 请参阅Java语言规范的8.2和8.3节。

(my emphasis). (我的重点)。 Similar language is explicitly in the documentation for getDeclaredMethods(), but not in that for getDeclaredClasses() (but can IMO be considered implicit there). 类似的语言在getDeclaredMethods()的文档中有明确说明,但在getDeclaredClasses()的文档中没有(但可以将IMO视为隐含的)。

So no, you cannot rely on ordering from Java reflection on the JVM; 因此,不能,您不能依赖JVM上Java反射的顺序。 in practice, I have seen the order vary based on the architecture of the running JVM (32- vs. 64-bit). 在实践中,我已经看到顺序根据运行的JVM(32位和64位)的体系结构而有所不同。

If you really must initialize the objects in a particular order (why?), you could use a naming convention and sort manually; 如果确实必须按特定顺序初始化对象(为什么?),则可以使用命名约定并手动排序; however, it would probably be better to change the code to be order-independent. 但是,最好将代码更改为与顺序无关。

Update 更新资料

It appears that you may be able to get something from the Scala reflection API: 看来您可以从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)
  }
}

See the API docs : 请参阅API文档

Sorts the symbols included in this scope so that: 1) Symbols appear in the linearization order of their owners. 对包含在此范围内的符号进行排序,以便:1)符号以其所有者的线性化顺序出现。 2) Symbols with the same owner appear in same order of their declarations. 2)具有相同所有者的符号以其声明的顺序出现。 3) Synthetic members (eg getters/setters for vals/vars) might appear in arbitrary order. 3)合成成员(例如val / var的getter / setter)可能以任意顺序出现。

However, this will not be guaranteed to work in general, particularly for mixed Java/Scala projects (since there really is no way to get members of a Java class in declaration order). 但是,通常不能保证这会正常工作,尤其是对于Java / Scala混合项目(因为实际上没有办法按声明顺序获取Java类的成员)。 Also, be aware that Scala runtime reflection is not thread-safe and generally not considered production ready. 另外,请注意,Scala运行时反射不是线程安全的,并且通常不认为可用于生产。

I still feel that you would be better served by modifying your design to be order independent, possibly by encoding the dependencies differently. 我仍然认为,将设计修改为与订单无关,最好是通过对依赖项进行不同编码来更好地为您服务。

AFAIK, this is impossible. AFAIK,这是不可能的。 The order class members are defined in simply isn't preserved in the .class files (at least using Java reflection). 订单类成员的定义根本不会保留在.class文件中(至少使用Java反射)。

You have a class with a few inner object s. 您有一个带有一些内部object s的类。 As you indicate, inner objects are lazily-initialised when some other code references/calls them (similar to lazy val ). 正如您所指出的,当其他一些代码引用/调用内部对象时,它们会被延迟初始化 (类似于lazy val )。

Problem: (a) aforementioned lazy-initialisation PLUS (b) implicit inter-dependencies between the object definitions. 问题: (a)前述的延迟初始化加(b)对象定义之间的隐式相互依赖关系。 This means that they need to be initialised in the order of their dependency-chain, but they do not explicitly reference each other, so correctly ordered lazy-initialisation does not occur automatically. 这意味着它们需要按照它们的依赖关系链的顺序进行初始化,但是它们没有明确地相互引用,因此不会自动进行正确顺序的延迟初始化。

Attempted solution: programmatically pre-initialise these objects in the correct order, by using reflection during construction/initialisation phase. 尝试的解决方案:通过在构造/初始化阶段使用反射,以正确的顺序以编程方式预先初始化这些对象。 While this would work if scala or java reflection worked with you, it 'goes against the grain' - it works counter to the very definition of the inner objects. 如果scala或java反射与您一起工作会起作用,但它“违背了规则”-与内部对象的定义完全相反。 If you don't want them to be lazy-initialised, then why declare them to be inner objects in the first place? 如果您不希望它们被延迟初始化,那么为什么首先要声明它们是内部对象呢?

My suggested solution: change the declarations from inner object s to val s. 我建议的解决方案:将声明从内部object s更改为val Order these declarations according to desired initialisation sequence. 根据所需的初始化顺序订购这些声明。 No reflection required. 无需反射。

(Aside: In this Q & the related ones, you've mentioned no artificial constraint or reason why object s must be used. However, if there is some strange reason why you must use object, then you still can avoid reflection. Simply have each object's constructor call a method forceInit of each object it depends upon. Each forceInit method could just return 1. This would trigger lazy-initialisation in the correct order.) (此外:在本问答中,您没有提到任何人为的约束或必须使用object s的原因。但是, 如果由于某些奇怪的原因而必须使用对象,则仍然可以避免反射。每个对象的构造函数都调用它所依赖的每个对象的方法forceInit 。每个forceInit方法只能返回1。这将以正确的顺序触发延迟初始化。)

:-) :-)

I ran into a similar problem and used annotations with some value to mandate the order. 我遇到了一个类似的问题,并使用具有一定价值的注释来强制执行该命令。 In particular, my object model drives an SWT table so I created: 特别是,我的对象模型驱动了SWT表,因此我创建了:

public @interface DefaultColumnPosition {
    int value();
}

Then annotate the fields with @DefaultColumnPosition(0) or whatever the number is. 然后用@DefaultColumnPosition(0)或任何数字注释字段。 Sort by the values. 按值排序。 There is a little bit of overhead and it would be better if there was a different method, or perhaps a class level annotation that enforced order but doesn't look like it. 会有一些开销,如果有其他方法,或者可能是强制执行顺序但看上去不像这样的类级注释,那会更好。

Have not tried the Scala solution but it looks interesting as well, I may give that a shot. 还没有尝试过Scala解决方案,但它看起来也很有趣,我可以试一试。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM