8

I have a class with a few objects as inner methods.

I also asked this question a while ago and got a good answer, but that leads to fatal errors in servlet containers. Scala cannot consistently generate a TypeTag when the URLClassLoader asks for a class.

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) ?

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.
  • getMethods or getDeclaredFields is known not to guarantee order. If this is somehow possible, it's likely with Scala reflection.
Community
  • 1
  • 1
flavian
  • 28,161
  • 11
  • 65
  • 105
  • I see `AbstractColumn` uses `getSimpleName`. Are you aware of SI-2034? – som-snytt Mar 20 '14 at 16:16
  • @som-snytt It's a valid point, but the double nesting is useless when using phantom. You'd have to do it on purpose to cause the error. Anyway, if I'm missing some scenarios please submit a bug via GitHub. – flavian Mar 20 '14 at 16:20

4 Answers4

5

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

public Field[] getDeclaredFields() throws SecurityException

Returns an array of Field objects reflecting all the fields declared by the class or interface represented by this Class object. 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. See The Java Language Specification, sections 8.2 and 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).

So no, you cannot rely on ordering from Java reflection on the JVM; in practice, I have seen the order vary based on the architecture of the running JVM (32- vs. 64-bit).

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:

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:

Sorts the symbols included in this scope so that: 1) Symbols appear in the linearization order of their owners. 2) Symbols with the same owner appear in same order of their declarations. 3) Synthetic members (e.g. getters/setters for vals/vars) might appear in arbitrary order.

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). Also, be aware that Scala runtime reflection is not thread-safe and generally not considered production ready.

I still feel that you would be better served by modifying your design to be order independent, possibly by encoding the dependencies differently.

espenhw
  • 871
  • 8
  • 8
  • You are telling me things I already know and I wouldn't ask the question and give 150 bounty if order was not important. Don't take this the wrong way, it looks like a tough one that can only be done with Scala reflection rather than Java – flavian Mar 18 '14 at 11:52
  • Updated my answer to account for the .sorted method on MemberScope, maybe that will work for you. – espenhw Mar 18 '14 at 12:25
  • sorted refers to lexicographic sorting or something similar, not to preserving order.. – flavian Mar 18 '14 at 12:43
  • Actually, as noted in the comment on the example code, the sorted method here is exactly declaration order. I updated the answer (again) with a link to the API docs and a relevant quote. – espenhw Mar 18 '14 at 12:52
0

AFAIK, this is impossible. The order class members are defined in simply isn't preserved in the .class files (at least using Java reflection).

Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487
  • It is quite easy with Scala reflection. https://github.com/newzly/phantom/blob/35824e4b219e1e2ed9533cd15e2212e13775bf7f/phantom-dsl/src/main/scala/com/newzly/phantom/EarlyInit.scala However, this gives the TypeTag problem. – flavian Mar 14 '14 at 13:27
  • @flavian, does Scala reflection actually guarantee that order is preserved? To me it sounds like it does the same thing as in java, namely that it's possible that the class members are returned in order, and it happens in some JDKs/implementations, but it's never guaranteed and should not be relied on. – eis Mar 18 '14 at 11:55
  • @eis I'm not 100% sure, I think I got the wining lottery ticket on OpenJDK 7 and Oracle Java 7, where hundreds of builds extensively testing order and Cassandra queries relying on that order passed with 100% success rate. The project in question features automagical CQL 3 schema generation directly from Scala code. Advanced CQL features rely on fields to be collected in the order they are written. Anyway, looks like I have to re-think the automagical. – flavian Mar 18 '14 at 12:07
0

You have a class with a few inner objects. As you indicate, inner objects are lazily-initialised when some other code references/calls them (similar to lazy val).

Problem: (a) aforementioned lazy-initialisation PLUS (b) implicit inter-dependencies between the object definitions. 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. 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 objects to vals. 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 objects 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.)

:-)

Glen Best
  • 22,769
  • 3
  • 58
  • 74
  • The suggested solution sounds good in theory, but if the objects are lazy evaled so is their constructor. Means you still need something to force init in order. vals cannot be used in my case. – flavian Mar 20 '14 at 11:15
  • If you can't use val (why??), then yes you need something to force init in order. The idea of calling forceInit is that you don't aggressively pre-construct all objects during program initialisation. Instead, you wait until any of the objects is naturally used by the program - it will be lazily constructed & will call forceInit of all dependencies so they will be constructed first. – Glen Best Mar 26 '14 at 01:53
0

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:

public @interface DefaultColumnPosition {
    int value();
}

Then annotate the fields with @DefaultColumnPosition(0) or whatever the number is. 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.

mike
  • 1,318
  • 3
  • 21
  • 41