Rationale behind constructor call chain... ( and comparison with C++)

Discussion in 'Java' started by A.B., Oct 30, 2006.

  1. A.B.

    A.B. Guest

    Just a question...

    If an object B extends an object A, whenever B is constructed, A is
    constructed... if A() calls a
    function that is overloaded in B, it looks like the overloaded function
    is called by the A constructor ...

    ( In C++ the behavior is always to call the function defined in A when
    called within the A constructor, even if the overloaded function is B
    is virtual. )

    Can someone explain me the rationale behind java's behavior in that
    case, how it's useful, more logical etc than the C++ approach... can
    this behavior be overriden?

    Thanks!
    A.B., Oct 30, 2006
    #1
    1. Advertising

  2. Re: Rationale behind constructor call chain... ( and comparison withC++)

    A.B. wrote:
    > Just a question...
    >
    > If an object B extends an object A, whenever B is constructed, A is
    > constructed... if A() calls a
    > function that is overloaded in B, it looks like the overloaded function
    > is called by the A constructor ...


    I think you mean class B and class A. If B extends A, then an instance
    of B is itself an instance of A.


    > ( In C++ the behavior is always to call the function defined in A when
    > called within the A constructor, even if the overloaded function is B
    > is virtual. )


    And abort(?) if the method is abstract.

    > Can someone explain me the rationale behind java's behavior in that
    > case, how it's useful, more logical etc than the C++ approach... can
    > this behavior be overriden?


    The idea of overriding is that the base classes method is overridden
    always. It does cause problems in that you can have a method called
    which tries to use fields that have not been initialised yet, but it's
    better to fail obviously in development than do something subtly wrong
    through to production.

    This means that factory methods work from the constructor. For instance
    you'll often see in Swing components have protected create methods that
    are called from the constructor.

    If you really wanted to, you could add an "initialised" boolean
    variable. Set it after the super constructor is called. In you
    overriding methods, if it has not been set just call the super method
    and exit. I'd be interested to see any uses of this, as in nine years of
    Java programming I have not come across one.

    Tom Hawtin
    Thomas Hawtin, Oct 30, 2006
    #2
    1. Advertising

  3. On Oct 30, 9:56 pm, "A.B." <> wrote:
    > Just a question...
    >
    > If an object B extends an object A, whenever B is constructed, A is
    > constructed... if A() calls a
    > function that is overloaded in B, it looks like the overloaded function
    > is called by the A constructor ...
    >
    > ( In C++ the behavior is always to call the function defined in A when
    > called within the A constructor, even if the overloaded function is B
    > is virtual. )
    >
    > Can someone explain me the rationale behind java's behavior in that
    > case, how it's useful, more logical etc than the C++ approach... can
    > this behavior be overriden?
    >
    > Thanks!


    The C++ approach is correct for C++.
    The Java(and C#) approach is correct for Java(and C#).

    The languages deal with calls to virtual methods during constructor
    calls in ways that make sense for those languages.

    Its not a question over which is a better way.

    In any case, its commonly understood to be 'a bad thing' in all 3
    languages.

    The 'Effective C++ book warns against it', see Chapter 2, Item 9: Never
    call virtual functions during construction or destruction. page 48.

    The same is true for Java and C# (but I can't google a good source for
    you to see).

    On the other hand, here's a Java RFI that was rejected....with
    reasons...

    http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4378291

    Regards

    Andrew
    andrewmcdonagh, Oct 30, 2006
    #3
  4. A.B.

    Eric Sosman Guest

    Re: Rationale behind constructor call chain... ( and comparison withC++)

    A.B. wrote:

    > Just a question...
    >
    > If an object B extends an object A, whenever B is constructed, A is
    > constructed... if A() calls a
    > function that is overloaded in B, it looks like the overloaded function
    > is called by the A constructor ...


    No, that never happens. A's constructor never calls an
    overloaded method of class B.

    Of course, I'm mocking you a little bit here. A's constructor
    might call a method that is over*ridden* by class B, if the coders
    of A and/or B were careless. But please: If you're going to debate
    fine points of the language, proper use of the existing nomenclature
    will be an aid to fruitful discussion.

    > ( In C++ the behavior is always to call the function defined in A when
    > called within the A constructor, even if the overloaded function is B
    > is virtual. )


    Can't comment; have avoided C++ with the same unreasoning
    hatred that kept me away from COBOL.

    > Can someone explain me the rationale behind java's behavior in that
    > case, how it's useful, more logical etc than the C++ approach... can
    > this behavior be overriden?


    At a guess, the rationale is simplicity, aka uniformity. As
    things stand, the rules for routing method calls are the same within
    constructors as they are everywhere else, and special cases are not
    required. If I understand your description of C++ behavior correctly,
    it seems there must be a "constructor mode" during which the rules
    behave differently. Further, it seems this mode must actually be
    dynamic: if method M1 calls method M2 but M1 can be called both
    from a constructor and from another post-construction method, is
    it supposed to choose A's native M2 during A construction and B's
    overriding M2 thereafter? Ay-ay-ay!... If that's how C++ actually
    behaves, I think I've been right to avoid it.

    --
    Eric Sosman
    lid
    Eric Sosman, Oct 31, 2006
    #4
  5. A.B.

    Chris Uppal Guest

    A.B. wrote:

    > ( In C++ the behavior is always to call the function defined in A when
    > called within the A constructor, even if the overloaded function is B
    > is virtual. )
    >
    > Can someone explain me the rationale behind java's behavior in that
    > case, how it's useful, more logical etc than the C++ approach... can
    > this behavior be overriden?


    The answer to the last question is no. If you want "special" semantics during
    initialisation then you will have to write code to implement the necessary
    checks and conditional behaviour.

    I think there are two possible reasons why Java didn't follow C++ in this (when
    it did borrow a fair amount of other stuff).

    One is that it makes the semantics simpler -- there are no special cases to
    describe or worry about. Both the C++ semantics and the Java semantics make
    some things easier but other things harder. So, given the choice between two
    options, both of which have advantages and disadvantages in roughly equal
    measure, it seem sensible to choose the simpler and more uniform of the two.

    The other is that C++'s semantics are difficult to implement without committing
    the language to using a vtable-based implementation of method dispatch. That
    is inefficient on most real-world processors. (It would be /possible/ to
    implement C++ semantics without vtables, but it'd be complicated to do
    efficiently.)

    -- chris
    Chris Uppal, Oct 31, 2006
    #5
  6. "A.B." <> writes:

    > ( In C++ the behavior is always to call the function defined in A when
    > called within the A constructor, even if the overloaded function is B
    > is virtual. )


    That's because C++ is an ugly hack on top of C. Since C does not "see"
    that B::foo() is a candidate for A::foo() until B is compiled and has
    its vptr table filled, A's constructor can only call the method it
    sees, which is A::foo().

    C++ programs run on platforms that lack the virtual call instruction
    of the Java VM.

    > Can someone explain me the rationale behind java's behavior in that
    > case, how it's useful, more logical etc than the C++ approach... can
    > this behavior be overriden?


    Yes, use a different language, there are hundreds out there. :p

    Constructors should ONLY be used for field initialization. If you call
    any methods that can be overridden (i.e. anything not declared
    private, final or static), you should document this silly side-effect
    of yours.
    Tor Iver Wilhelmsen, Oct 31, 2006
    #6
  7. Re: Rationale behind constructor call chain... ( and comparison withC++)

    Tor Iver Wilhelmsen wrote:
    > "A.B." <> writes:
    >
    >> ( In C++ the behavior is always to call the function defined in A when
    >> called within the A constructor, even if the overloaded function is B
    >> is virtual. )

    >
    > That's because C++ is an ugly hack on top of C. Since C does not "see"
    > that B::foo() is a candidate for A::foo() until B is compiled and has
    > its vptr table filled, A's constructor can only call the method it
    > sees, which is A::foo().


    I don't quite follow. Any function in A that calls a virtual function on
    this (and it isn't in the constructor) will run the overridden version.

    A possible explanation is that the C++ -> C bindings used a single
    function for the constructor, whether it was called from a subclass
    constructor or not. The constructor needs to set the vtable pointer. If
    it sets the pointer before calling the base class constructor, then the
    base class will just overwrite it. Therefore, the vtable pointer needs
    to be written after the base class constructor is invoked, and the
    behaviour follows.

    In JVM bytecode, creating an instance of a class takes two instructions:
    new to allocate and invokespecial to call the most derived class'
    constructor. On pre-1.6 JVMs, you could throw an exception before
    calling the super constructor, and the finalizer would still run with a
    full set of 'virtual' methods (From 1.5 the Object construct must exit
    normally in order for the finalizer to run).

    I don't recall Stroustrup's The Design and Evolution of C++ mentioning
    this issue.

    > C++ programs run on platforms that lack the virtual call instruction
    > of the Java VM.


    What virtual call instruction? Can you name some platforms?

    Tom Hawtin
    Thomas Hawtin, Oct 31, 2006
    #7
  8. Re: Rationale behind constructor call chain... ( and comparison withC++)

    Tor Iver Wilhelmsen wrote:
    > Thomas Hawtin <> writes:
    >
    >> What virtual call instruction? Can you name some platforms?

    >
    > The "invokevirtual" instruction (0xb6) as described in the JVM spec,
    > section 7.7.
    >
    > Platforms: Sun's implementation is the most widely used.


    Now I'm confused.

    If you were talking about the Java platform, what has that got to do
    with C++?

    You could target the 6502 with a Java bytecode compiler, but that has no
    invokevirtual instruction.

    Tom Hawtin
    Thomas Hawtin, Nov 1, 2006
    #8
  9. Thomas Hawtin <> writes:

    > What virtual call instruction? Can you name some platforms?


    The "invokevirtual" instruction (0xb6) as described in the JVM spec,
    section 7.7.

    Platforms: Sun's implementation is the most widely used.
    Tor Iver Wilhelmsen, Nov 1, 2006
    #9
  10. A.B.

    EJP Guest

    Re: Rationale behind constructor call chain... ( and comparison withC++)

    Thomas Hawtin wrote:

    >> ( In C++ the behavior is always to call the function defined in A when
    >> called within the A constructor, even if the overloaded function is B
    >> is virtual. )

    >
    > And abort(?) if the method is abstract.


    No, give a compilation error because the method is still abstract (C++
    pure virtual) at the point of this invocation. Personally I prefer the
    C++ behaviour. You shouldn't be able to do anything to a derived class
    until the base class is completely constructed.
    EJP, Nov 2, 2006
    #10
  11. Re: Rationale behind constructor call chain... ( and comparison withC++)

    EJP wrote:
    > Thomas Hawtin wrote:
    >
    >>> ( In C++ the behavior is always to call the function defined in A when
    >>> called within the A constructor, even if the overloaded function is B
    >>> is virtual. )

    >>
    >> And abort(?) if the method is abstract.

    >
    > No, give a compilation error because the method is still abstract (C++
    > pure virtual) at the point of this invocation. Personally I prefer the
    > C++ behaviour. You shouldn't be able to do anything to a derived class
    > until the base class is completely constructed.


    I believe it's legal C++. A more likely scenario is you call a function
    that calls the abstract function.

    The Sun Studio 11 C++ compiler gives me a warning (not an error) if I
    call a pure virtual function directly. Nothing is reported if I call it
    indirectly.

    My test program (a.cpp):

    class Base {
    public:
    virtual void fn() = 0;
    void gn() {
    fn();
    }
    Base() {
    gn();
    fn();//
    }
    };
    void Base::fn() {
    }
    class Derived : Base {
    public:
    Derived() : Base() {
    }
    void fn() {
    }
    };

    int main() {
    Derived d;
    }

    Warning message (provide the call to fn is not commented out):

    "a.cpp", line 9: Warning: Attempt to call a pure virtual function
    Base::fn() will always fail.
    "a.cpp", line 9: Warning: Attempt to call a pure virtual function
    Base::fn() will always fail.
    2 Warning(s) detected.

    I'm not sure why the warning is printed twice. Perhaps the compiler
    thought it was important.

    When run (whether the call to fn is commented out or not, and even with
    an implementation of the pure virtual function supplied):

    Pure virtual function called
    Abort (core dumped)

    Tom Hawtin
    Thomas Hawtin, Nov 2, 2006
    #11
  12. Thomas Hawtin <> writes:

    > If you were talking about the Java platform, what has that got to do
    > with C++?


    It was an example of how the platforms were different. This thread
    started with someone complaining that (virtual) method calls in Java
    behaved differently from C++. My claim is that this is not a problem
    unless you want all languages to behave the same, in which all you
    need is one.

    > You could target the 6502 with a Java bytecode compiler, but that
    > has no invokevirtual instruction.


    Really? All VMs I've seen for that small processors run interpreted,
    not byte-compiled.

    http://www.mts.net/~kbagnall/commodore/java.html
    Tor Iver Wilhelmsen, Nov 2, 2006
    #12
  13. Re: Rationale behind constructor call chain... ( and comparison withC++)

    Tor Iver Wilhelmsen wrote:
    > Thomas Hawtin <> writes:
    >
    >> You could target the 6502 with a Java bytecode compiler, but that
    >> has no invokevirtual instruction.

    >
    > Really? All VMs I've seen for that small processors run interpreted,
    > not byte-compiled.
    >
    > http://www.mts.net/~kbagnall/commodore/java.html


    YA RLY

    There are a number of reasons why you'd want to interpret on a small
    platform. Probably the most important is that it requires much less
    effort to write an interpreter over a compiler. It has nothing to do
    with whether there is a one-to-one correspondence between JVM and
    machine instructions.

    I've seen a suspicious lack of details about what happens with JavaCard
    implementations, although it does seem set up for cross compilation.

    So, invokevirtual in 6502 (assuming we're not using "sideways" memory or
    anything like that)

    ; zero page location
    obj = 0 ; object to call
    tmpA = 2 ; a temporary

    ; invokevirtual
    ; JSR to push return code onto stack first
    ; There is no JSR indirect
    ; (at least not in classic NMOS Mostek 6502).
    JSR invokeCode
    ...

    ..invokeCode
    ; Called with return address on stack.
    ; We push method address onto stack (so two addresses)
    ; and RTS to jump to it.

    ; load vtbl/class from obj
    LDY #0
    LDA (obj), Y
    STA tmpA+0
    INY
    LDA (obj), Y
    STA tmpA+1
    ; tmpA now points to obj.vtbl

    ; push method address onto stack
    ; I think this is the right way around...
    ; Not sure if vtbl requires a fixed offset from actual address.
    ; An alternative method is to store and JMP indirect
    LDY #methodOffset+1
    LDA (tmpA), Y
    PHA
    DEY
    LDA (tmpA), Y
    PHA

    ; not return - jump to method
    RTS

    6502 was a 1980s thing for me, so I'm a bit rusty.

    Actually checking my instructions (I used X for indirect postindexing,
    at first), I see using RTS for an indirect jump is a standard idiom.

    http://www.atarimax.com/jindroush.atari.org/aopc.html#RTS

    Tom Hawtin
    Thomas Hawtin, Nov 2, 2006
    #13
  14. Re: Rationale behind constructor call chain... ( and comparison withC++)

    Thomas Hawtin wrote:
    > Tor Iver Wilhelmsen wrote:
    >> Thomas Hawtin <> writes:
    >>> You could target the 6502 with a Java bytecode compiler, but that
    >>> has no invokevirtual instruction.

    >>
    >> Really? All VMs I've seen for that small processors run interpreted,
    >> not byte-compiled.


    > There are a number of reasons why you'd want to interpret on a small
    > platform. Probably the most important is that it requires much less
    > effort to write an interpreter over a compiler. It has nothing to do
    > with whether there is a one-to-one correspondence between JVM and
    > machine instructions.


    ????

    http://java.sun.com/docs/books/vmspec/2nd-edition/html/Overview.doc.html#31293

    is very clear: JVM's has an invokevirtual instruction for
    the Java byte code.

    It does not matter whether the JVM interprets that instruction or
    JIT compiles it to some native instructions.

    The instruction is valid byte code or it is not a JVM.

    Arne
    =?ISO-8859-1?Q?Arne_Vajh=F8j?=, Nov 3, 2006
    #14
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Alexander Grigoriev
    Replies:
    0
    Views:
    376
    Alexander Grigoriev
    Sep 12, 2003
  2. Asfand Yar Qazi
    Replies:
    5
    Views:
    358
    Siemel Naran
    Apr 7, 2004
  3. Fernando Perez
    Replies:
    18
    Views:
    633
    Steven Bethard
    Dec 10, 2004
  4. dragoncoder
    Replies:
    35
    Views:
    2,205
    Mirek Fidler
    Dec 17, 2006
  5. Noob
    Replies:
    25
    Views:
    1,449
    Nick Keighley
    Dec 9, 2009
Loading...

Share This Page