Serialization quiz
brian
18 Jun 2012
I think the problem there is that your field initializer for Child.i runs after Parent.make calls your function. So you need to move the it-block function call into Child.
Akcelisto
18 Jun 2012
I suppose that field initializers runs before constructor call:
class C{
Int i := 0
new make(){
i = 1
}
static Void main(){
echo(c.i)
}
}
Prints:
1
But if I write that:
class A{
new make(){
trap("i", [1])
}
}
class B:A{
Int i := 0
new make(){ // why parent ctor not called?
}
}
class SerialTest : Test{
Void testBefore(){
verifyEq(1, B()->i)
}
}
Output:
TEST FAILED sys::TestErr: Test failed: 1 [sys::Int] != 0 [sys::Int]
What is sequence of calling ctors and field initializers?
KevinKelley
18 Jun 2012
Good one. There's some consolidation of initializers that happens, but I'm not sure I can explain it.
- The full object is created, zero-initialized, before you can see it.
- constructor sequence is next, base to derived. In each class, field initializers are consolidated into a <init> block; each such block runs before any constructor for that class.
- the compiler enforces that the
this/superconstructor-chaining happens in correct order. You can chain within a class however you like, but the chain has to follow the super-first rule. withblocks throw some confusion into the mix, because they happen (usually) during the base-class constructor execution, before the subclass initializers have run.
It's certainly possible I've screwed this up; it still confuses me. I don't even remember now what happens with virtual method calls in a constructor, please don't go there next. :-)
In your trap("i", [1]) above, then, adding an echo(this->i) shows that it is setting the derived member, but the next step is to enter the derived constructor chain, overwriting it.
That's my story, anyway.
Akcelisto
19 Jun 2012
I think, current creation sequence is:
- obj creation
superfields initializersuperctorthisfields initializerthisctor
Better if creation sequence will be:
- obj creation
superfields initializerthisfields initializersuperctorthisctor
brian
20 Jun 2012
Current sequence is correct. As a general principle you really want to fully initialize the base class before initializing a subclass. This is how it works in Java too. The complexity of constructors is one of many reasons I really dislike inheritance - I hardly use inheritance in my code at all these days.
Akcelisto
18 Jun 2012
I want create abstract class with serializable childs. But child deserialization not work.
Why this test failed?
class SerialTest : Test{ Void testChange(){ c := Child() c.i = 1 sb := StrBuf() sb.out.writeObj(c) verifyEq(1, sb.toStr.in.readObj->i) } } @Serializable abstract class Parent{ new make(|This|? f:=null){ f?.call(this) } } class Child : Parent{ Int i := 0 new make(|This|? f:=null):super.make(f){ } }Output: