• Post Reply Bookmark Topic Watch Topic
  • New Topic

Selection of invocation of overridden method during constructor chaining  RSS feed

 
Bora Sabrioglu
Ranch Hand
Posts: 100
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hey people,

I don't understand, why when in the constructor of the superclass A the method init() is called (line 4), the overridden version of the subclass is invoked (line 26) and not the superclass version (line 7):



I would have guessed that above code prints
test
1

But instead you get a NPE (because when the constructor of B is invoked

then there is first the implicit call to super:

which is the constructor of A:

but here now this init() method is not the version of A () but the overriden version of the subclass (B): ... which throws an NPE of course, because the initialization of s has not occured yet (it happens only after the implicit call to super() has finished (see ))

Does anyone know why this happens?

Thanks in advance...
 
Winston Gutkowski
Bartender
Posts: 10575
66
Eclipse IDE Hibernate Ubuntu
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Bora Sabrioglu wrote:Does anyone know why this happens?

I don't understand the question. You've just given a perfect explanation of it yourself. The class being created is a B, therefore any call to init() will run B.init(), which exhibits exactly the behaviour you just described.

If you're asking "why did the architects do it that way?", my question to you would be: What else would you have it do?

Winston
 
Stephan van Hulst
Saloon Keeper
Posts: 7969
143
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Note that you should probably never call overridable methods from a constructor, or pass this as an argument to methods.

If you need to call a method of the class being constructed, make that method private or final.
 
Bora Sabrioglu
Ranch Hand
Posts: 100
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Winston Gutkowski wrote:If you're asking "why did the architects do it that way?"...


Oh no, that's not what I meant. I asked the question, because I'm just a little clueless and confused, that's all.
I don't understand what seems very clear to you:
The class being created is a B, therefore any call to init() will run B.init(), which exhibits exactly the behaviour you just described.


So far I haven't read anything about this and that's why it's new to me. I (falsely) assumed, that because the init method is called inside the constructor of A and A has its own init method, that A.init() will be chosen.

If you have

and then somewhere you write then because the object is of type A , the A version of init() will be invoked. And if you have then now, because the object is of type B , the B version of init() will be invoked. (That's because of polymorphism.)

Does the same rule apply here in the initial constructor example as well (where you just construct an object of type B and don't assign that object to any reference and later in the superconstructor you invoke init() without any reference and the dot operator)? and
 
Paul Clapham
Sheriff
Posts: 22819
43
Eclipse IDE Firefox Browser MySQL Database
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Bora Sabrioglu wrote:then now, because the object is of type B , the B version of init() will be invoked. (That's because of polymorphism.)


This is all you need to know. Whether the code which calls the init() method is in a constructor -- or a method -- declared in a superclass of B has nothing to do with it. It seems you assumed that constructors should work differently than methods as far as polymorphism is concerned, or perhaps you hadn't yet got to thinking about calls from a method, but they don't.
 
Bora Sabrioglu
Ranch Hand
Posts: 100
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Paul Clapham wrote:
Bora Sabrioglu wrote:then now, because the object is of type B , the B version of init() will be invoked. (That's because of polymorphism.)


This is all you need to know.


Good to know.

Here is another way that helps me understand this better:

Since Java implicitly inserts, when there is a call to a method without a reference (like init()), a this reference (so init() becomes implicitly this.init()) implicitly becomes

And since this always refers to the current object and the object is of type B (because of new B();) the B version of init() will be called.

Thank you guys for helping me debug myself
 
Jesper de Jong
Java Cowboy
Sheriff
Posts: 16057
88
Android IntelliJ IDE Java Scala Spring
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I want to stress this important point that Stephan made:
Stephan van Hulst wrote:Note that you should probably never call overridable methods from a constructor, or pass this as an argument to methods.

The purpose of a constructor is to initialize a new object. If you are calling overridden methods from inside a constructor, then you are calling a method on an object that might not be fully initialized. That can lead to unexpected behaviour. For example: can you explain the output of the following program?

You should not let the 'this' reference escape outside of a constructor, because then there might be code outside of the constructor that gets exposed to an object that is not fully initialized. Besides the example above, there are other ways that you can get this kind of problem. For example by starting a thread inside the constructor that accesses the object's member variables.

 
Bora Sabrioglu
Ranch Hand
Posts: 100
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Jesper de Jong wrote: For example: can you explain the output of the following program?

It prints null since instance variables are initialized AFTER all superclass constructors complete... and since the call to init was made from within the superclass constructor, the attribute 'message' still holds the default value... which is null (see p. 469 SCJP6 Study Guide Bates/Sierra for reference).
 
Campbell Ritchie
Marshal
Posts: 56529
172
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Bora Sabrioglu wrote: . . . It prints null . . .
Not when I tried it, it didn't.
 
Bora Sabrioglu
Ranch Hand
Posts: 100
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Campbell Ritchie wrote:Not when I tried it, it didn't.

Now it gets interesting...
 
sreenivasarao mydukuri
Greenhorn
Posts: 7
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
HI Sabrioglu,

this is pretty simple, here we are doing inheritance, method overriding .
1) Class A init() method was Overridden by Class B int(),
2) The exact reason for getting Null pointer exception was Super class (Class A) constructor was calling sub class (Class B ) int() method.
public void init() {
System.out.println(s+=s.length());
}
At the time of executing class A constructor we didnt initialize variable S.


 
Bora Sabrioglu
Ranch Hand
Posts: 100
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
sreenivasarao mydukuri wrote:Super class (Class A) constructor was calling sub class (Class B ) int() method.

Yes. That was the point where I falsely assumed that the super class is calling its own init() method. Now I know better.
 
Bora Sabrioglu
Ranch Hand
Posts: 100
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Campbell Ritchie wrote:
Bora Sabrioglu wrote: . . . It prints null . . .
Not when I tried it, it didn't.

So what do you get as output? ...
 
Campbell Ritchie
Marshal
Posts: 56529
172
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Nothing. I got a NullPointerException instead.
 
sreenivasarao mydukuri
Greenhorn
Posts: 7
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Campbell Ritchie wrote:Nothing. I got a NullPointerException instead.


yes it will return null pointer exception because we are accessing string with out initialization......... it is correct.
 
Bora Sabrioglu
Ranch Hand
Posts: 100
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Campbell Ritchie wrote:Nothing. I got a NullPointerException instead.

Did you run the example of Jesper de Jong or the initial example in the first post of the OP (which is me)?
If you tried Jespers example and get an NPE then I really have no idea why this happens...
If you tried my example (the first post), then yes, as the previous poster correctly mentioned, the reason is trying to call the length() method on a String variable with the value 'null', since uninitialized.
 
Campbell Ritchie
Marshal
Posts: 56529
172
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Jesper's example. I was fully expecting it to print “null”.
 
Campbell Ritchie
Marshal
Posts: 56529
172
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I have tried it again and it printed “null”, which is what I expected earlier.
 
Bora Sabrioglu
Ranch Hand
Posts: 100
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
My state went from confused to (thought-that-)I-got-it back to confused again.

Stephan van Hulst wrote:If you need to call a method of the class being constructed, make that method private or final.

I did just that, the only difference to the initial example is in line 7, I just changed the modifier from public to private:

And now I don't understand, why the A.init() method is chosen over the B.init() method... :/
(now the program behaves EXACTLY the way I INITIALLY expected it to behave (that the A.init() method is invoked and not the B.init() version... that's why I opened this topic in the first place)

Now the B version clearly is not an overridden version of the A version, since class B doesn't even see the init() method of A (since is is marked private). So the B.init() is just a normal method of B that happens to have the same name as init() in A, but has no relationship to it whatsoever.

Am I right in assuming that the new insight that I had due to the posts above, that always the type of the object determines which version of a method is invoked (if object of type B, the B's init() is invoked) is only valid in the case of overriding? Or is this a wrong assumption?

And again, if you just implicitly insert a 'this.' in front of the call to init() in the A constructor, the current object is of type B, so why is not the B version invoked?
 
Campbell Ritchie
Marshal
Posts: 56529
172
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
You need all the help you can get. For years programmers complained about little errors like trying to override the tostring() method and that it was not obvious why that didn't work. Then, in Java5, the @Override annotation was introduced which solves that problem. Add the @Override annotation to every method you think is an overriding method, and see what happens. You will need to recompile everything.
 
Campbell Ritchie
Marshal
Posts: 56529
172
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
While you work out the @Override annotation, I shall see whether I can work out why I got the Exception rather than null, which I expected. Maybe I copied your version, but I thought I had copied Jesper's version. One of those little mysteries of life. Since I overwrote the .java files, I have destroyed the evidence. So we shall never know. I must have copied something wrongly.
 
Bora Sabrioglu
Ranch Hand
Posts: 100
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Campbell Ritchie wrote:Add the @Override annotation to every method you think is an overriding method, and see what happens.

Ah, thanks for the hint... now I know what this Annotation is good for
I get:
error: method does not override or implement a method from a supertype

Since the superclass's init() was marked private, there was no way the subclass's init() could override the superclass's init(). I understand that.
What I DON'T understand is:

Why is now, when I mark A's init() as PRIVATE, A's init() invoked and not B's init()?

Since
implicitly becomes

with 'this' referring to an object of type B. That's why I would assume that B's init() is invoked, but it's not. A's init() is invoked... why does that happen if I mark A's init() as private?
(I think that I understood the case when it's marked public: then B's init() is overriding A's init(). And since the object being constructed is of type B ('this' refers to an object of type B) B's init() is invoked. But here in this case, when we mark A's init() as private something else is going on which leads to the invocation of A's init(). I would like to understand what that 'something' is.)
 
Campbell Ritchie
Marshal
Posts: 56529
172
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Because init in A is private, init in B cannot override it. The A constructor can only therefore call the A version.
 
Bora Sabrioglu
Ranch Hand
Posts: 100
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Campbell Ritchie wrote:Because init in A is private, init in B cannot override it.
Right.
Campbell Ritchie wrote: The A constructor can only therefore call the A version.
Okay... so it seems that the assumption that I made above was correct?
Am I right in assuming that the new insight that I had due to the posts above, that always the type of the object determines which version of a method is invoked (if object of type B, the B's init() is invoked) is only valid in the case of overriding? Or is this a wrong assumption?

And when I think about it in conclusion, what we were talking about all along was basically polymorphic method invocations... and these are known to work only with overridden methods. And since in the case of marking init private there is no overriding => there is no polymorphism, i.e. the decision of which method to call won't be made based on the type of the object anymore, or: init of the subclass (B) won't be called, since it is not overriding init of A.

p.102 SCJP6 Study Guide Bates / Sierra says: "Only overridden instance methods are dynamically invoked based on the real object's type."

I think that I finally got it
 
Campbell Ritchie
Marshal
Posts: 56529
172
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator


When Jesper said only to call private or final methods from the constructor it was because they cannot be overridden to give unexpected behaviour.
 
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!