2

There are two classes: A and B. B is the subclass of A. A stores a reference of B, which may in certain scenarios a reference to this as well.

public B b;

In the constructor of A, is it legal to cast the this reference to B (provided that I know that the concrete type of the object under construction is B)? (Syntactically, I know it is; design-wise, it probably reflects a bad component design; but I want to know if it has any severe risks and thus unacceptable.)

public A(...parameters) {
    b = (B) this;
}

The question has arisen because by this time, the constructor of B has not yet completed (because this runs exactly when B calls super).

I know that accesssing b before its constructor completes means that I'm accessing (semantically) uninitialized members (so I never do that), but is this all?

Thomas Calc
  • 2,994
  • 3
  • 30
  • 56
  • what if you have class `C extends A`, you will be casting `C to B` resulting `ClassCastException`? – Nandkumar Tekale Jan 08 '13 at 10:52
  • Good spot. I just edited my post at the same time you were sending the comment (sorry for the late edit). The edit was: When casting, I do know that the instance being constructed is of type B. – Thomas Calc Jan 08 '13 at 10:53
  • 8
    fact that A has knowledge of B, which is a subclass, tends to indicate a bad design, imho. – njzk2 Jan 08 '13 at 10:54
  • @njzk2: yeah, it basically goes against good design and OO principles; my post mentions this from the beginning. – Thomas Calc Jan 08 '13 at 10:55
  • 1
    so, to answer your question, it is legal (though can be dangerous), and works if this is indeed an instance of B. But you'd have to make sure of this. – njzk2 Jan 08 '13 at 10:56
  • If you are `saying the constructor of B has not yet completed` then by what reference you are passing arguments to constructor of A? – joey rohan Jan 08 '13 at 11:00
  • @joeyrohan: sorry, I don't understand your question. Constructor of A is called from the constructor of B. Super is the first instruction in B's constructor. The rest of initialization in B only comes after it called super(). – Thomas Calc Jan 08 '13 at 11:06
  • @joey: The constructor of superclasses is executed first and then afterwards the constructor of subclasses is executed. That means that while the constructor of A is executing, the constructor of B hasn't been executed yet (completely). On the other side, the reference is the same, because there is only one object. So the reference is "this" in A, which points to the very same object as "this" in B. – Daniel S. Jan 08 '13 at 11:08
  • 1
    @ThomasCalc, I enjoy your question! – Daniel S. Jan 08 '13 at 11:09
  • @Daniel: upvote to you for that. But why is it so enjoyable? :-) – Thomas Calc Jan 08 '13 at 11:11
  • @joey: I suppose your question is based on the fact that on some languages, I suppose the object in memory is not yet constructed before it's constructor ends. In Java/Android, when your constructor code runs, the memory layout of the object is already fully constructed in the memory (with default-initialized field values). – Thomas Calc Jan 08 '13 at 11:14
  • Thanks for clarification @DanielS. and Thomas . Well, if thats a case,i dont think any problem will arise except the answer – joey rohan Jan 08 '13 at 12:10

2 Answers2

1

- Well the problem will arise if there is any other class that also extends to A, then it may be casted by default to Class B.

- I would advice you to use instanceof to sort this thing out....

Kumar Vivek Mitra
  • 33,294
  • 6
  • 48
  • 75
1

This constellation does not break any other rules besides good OO design. Java is prepared for this case, because all fields of B get initialized even before any of the constructors get invoked, as shown by the following example:

public class A {

    int aMember;

    public static final void main(String[] args) {
        new B();
    }

    A() {
        B b = (B) this;

        System.out.println("in A(), before b.bMember    has been set here, b.bMember    = " + b.bMember);
        System.out.println("in A(), before this.aMember has been set here, this.aMember = " + this.aMember);

        this.aMember = 5;
        b.bMember = 1; // will be overwritten in B()

        System.out.println("in A(), after  b.bMember    has been set here, b.bMember    = " + b.bMember);
        System.out.println("in A(), after  this.aMember has been set here, this.aMember = " + this.aMember);
    }
}

--

public class B extends A {

    int bMember;

    B() {
        super(); // invokes A()

        System.out.println("in B(), before this.bMember has been set here, this.bMember = " + this.bMember);

        this.bMember = 6;

        System.out.println("in B(), after  this.bMember has been set here, this.bMember = " + this.bMember);
    }
}

This outputs:

in A(), before b.bMember    has been set here, b.bMember    = 0
in A(), before this.aMember has been set here, this.aMember = 0
in A(), after  b.bMember    has been set here, b.bMember    = 1
in A(), after  this.aMember has been set here, this.aMember = 5
in B(), before this.bMember has been set here, this.bMember = 1
in B(), after  this.bMember has been set here, this.bMember = 6

That means that in A(), B's member variables are simply in the same state as A's member variables before they get set. Both have been initialized to their defaults (0 for int, false for boolean, null for objects, ...) and can be used. A strong pitfall is that B's member variables can be set in A(), but can be overwritten by B's own constructor, which is very counterintuitive.

I like this question because it is expressed simply and clearly, yet it raises more complicated questions, which reach into other topics like compiler internals and details in object creation.

Daniel S.
  • 6,458
  • 4
  • 35
  • 78
  • Thanks for the useful and detailed train of thought. If you don't mind, I keep the other (short but correct) post marked as answer, because got marked as answer much sooner, and it wouldn't be fair for its author if I removed its Answer status now. – Thomas Calc Jan 08 '13 at 12:51
  • I had fun playing around with Java and thinking about the question, so this is completely fine for me and the way it should be. – Daniel S. Jan 08 '13 at 15:13