aspose file tools*
The moose likes Java in General and the fly likes Is this a bug in Eclipse's compiler? Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login
JavaRanch » Java Forums » Java » Java in General
Bookmark "Is this a bug in Eclipse Watch "Is this a bug in Eclipse New topic
Author

Is this a bug in Eclipse's compiler?

Frans Janssen
Ranch Hand

Joined: Dec 29, 2004
Posts: 357
When I migrated my IDE from JBuilder2005 to Eclipse I got a mysterious null pointer exception in my project that had never been there before. Here is a small piece of code that demonstrates the essence of the problem:


When compiled with Eclipse and run, I get the following error message:
Exception in thread "main" java.lang.NullPointerException
at Test.access$0(Test.java:4)
at Test$Inner.lalala(Test.java:20)
at Test$InnerSuper.<init>(Test.java:12)
at Test$Inner.<init>(Test.java:18)
at Test.<init>(Test.java:7)
at Test.main(Test.java:25)


Somehow Eclipse inserted a method called access$0 into my class that hits a NullPointerException. When I compile my class with JBuilder or javac it runs without problems.

I am aware that I am not supposed to call an overriden method from the InnerSuper's constructor, because the Inner's member variables won't be initialized when it's called. However in this case there are none (at least no explicit ones). It seems that there is some hidden code that establish a relation between the inner class and its surrounding class that is not yet initialized when the initialize() method is called from the constructor.

My question is: is this a bug in Eclipse and should I report it as such or is this legal behavior according to the Java standard and should I refactor my code (which I probably should anyway)?

Thanks for your thoughts,

Frans.


SCJP 1.4, SCJD
Rodrigo Alvarez
Ranch Hand

Joined: Apr 10, 2006
Posts: 75

InnerSuper() {
initialize();
}

abstract protected void initialize();


No no no no (I mean, no)

Hi there,

You should *never* call a polymorphic method from a constructor. Actually, it's considered a good practise to make final any method you call from the constructor.

Remember that the JVM will construct the instance by calling successively all the constructor of your hierarchy, from top to bottom. When you are in InnerSuper(), you have not yet created "the part of your object that makes it a Inner".

I'm not quite sure why it worked with you previous compiler, but it's definitely a bad thing to do.


It is a mistake to think you can solve any major problems just with potatoes.<br />--Douglas Adams
Edwin Dalorzo
Ranch Hand

Joined: Dec 31, 2004
Posts: 961
I pasted your code in my Eclipse 3.1.2 and it compiled and ran just fine.
Ken Blair
Ranch Hand

Joined: Jul 15, 2003
Posts: 1078
Did you setup Eclipse to compile for 5.0? That behavior looks like something that would have occurred in the old memory model but not in the new one. If JBuilder was configured to compile for 5.0 and Eclipse is not this would explain the sudden problem.

Regardless, what was said earlier about this being a big "no no" is correct and should be heeded.
Ilja Preuss
author
Sheriff

Joined: Jul 11, 2001
Posts: 14112

I am aware that I am not supposed to call an overriden method from the InnerSuper's constructor, because the Inner's member variables won't be initialized when it's called. However in this case there are none (at least no explicit ones).


You hit the nail on the head: there is an implicit one!

In the initialize method, you are accessing member, a private field of the outer class. Now you have to remember that the JVM actually doesn't know anything about inner classes - the compiler has to compile your code so that only top level classes are used. But that is a problem in that case - if Inner actually gets compiled to a top level class, how does it access the private field of the outer instance?

The solution is for the compiler to introduce a synthetic access method. That method is called through a synthetic field in the inner class - the reference to the outer instance.

So what that error message is telling you is that the reference to the outer instance isn't yet initialized.

And I think Ken is right - I seem to remember a discussion that they actually changed the language specification in this regard recently.


The soul is dyed the color of its thoughts. Think only on those things that are in line with your principles and can bear the light of day. The content of your character is your choice. Day by day, what you do is who you become. Your integrity is your destiny - it is the light that guides your way. - Heraclitus
Ken Blair
Ranch Hand

Joined: Jul 15, 2003
Posts: 1078
Originally posted by Ilja Preuss:
And I think Ken is right - I seem to remember a discussion that they actually changed the language specification in this regard recently.


The memory model changed drastically and part of that involved initialization. However, I wouldn't have expected that to affect this code. In fact, this code should work as I read it. Instance variable intializers are supposed to be executed before the method body. I only bring up the version because it's the only reason I can think of for it to suddenly stop working when switching to Eclipse when at least one Eclipse user has said it works fine for them.
Ilja Preuss
author
Sheriff

Joined: Jul 11, 2001
Posts: 14112
Originally posted by Ken Blair:
Instance variable intializers are supposed to be executed before the method body.


But the instance initializer for Inner isn't supposed to be executed before the constructor body of its superclass InnerSuper, from where the method is called, is it?
Ken Blair
Ranch Hand

Joined: Jul 15, 2003
Posts: 1078
What instance initializer in Inner are you referring to?
Tony Morris
Ranch Hand

Joined: Sep 24, 2003
Posts: 1608

Actually, it's considered a good practise to make final any method you call from the constructor.

Not quite.
It is considered a good practise to make any overrideable method that is called from a constructor, not overrideable. Or better, don't call any method*. An overrideable method is one that is:
* not declared final
* not declared static
* not declared private
* declared in a class that is not declared final
* declared in a top-level (not nested) class, the class exposes at least one constructor that is not declared private
Therefore, by failing to meet any one of the above criteria - not just "declare the method final" - you imply that your method is not overrideable.

*I, and a few others, consider it bad practise to do anything within a constructor besides throw an exception, but that's proven difficult to demonstrate to an arbitrary audience since it requires much externalisation.

Edit: inadvertant reverse logic fixed
[ April 14, 2006: Message edited by: Tony Morris ]

Tony Morris
Java Q&A (FAQ, Trivia)
Rodrigo Alvarez
Ranch Hand

Joined: Apr 10, 2006
Posts: 75

by failing to meet any one of the above criteria - not just "declare the method final" - you imply that your method is not overrideable.




Hi Tony,

Sorry for being such an arbitrary audience. I'm visibly far less intelligent than you so I'll probably have to stick to my "just-declare-the-method-final" policy, keeping in my head a "except if final is a non sense in this case, like when methods are private, static or in a class you can't sub-class" note. Although I do appreciate the tripple negation way you clarified things.

cheers,

Simon
Ilja Preuss
author
Sheriff

Joined: Jul 11, 2001
Posts: 14112
Originally posted by Ken Blair:
What instance initializer in Inner are you referring to?


Sorry, meant instance variable initializer (hope that's the correct term) - the one for the implicit reference to the "outer instance".
Frans Janssen
Ranch Hand

Joined: Dec 29, 2004
Posts: 357
Originally posted by Ken Blair:
Did you setup Eclipse to compile for 5.0? That behavior looks like something that would have occurred in the old memory model but not in the new one. If JBuilder was configured to compile for 5.0 and Eclipse is not this would explain the sudden problem.

Regardless, what was said earlier about this being a big "no no" is correct and should be heeded.


Eclipse was set for 1.4. Setting it to 5.0 did indeed solve it.
In JBuilder, launguage features had been set to 1.3 and earlier, but target VM was set to 1.4 and later. Setting the latter to "all VMs" would reproduce the exception also in JBuilder. (As would using the 1.4.2 javac.exe.)

As I mentioned in my original post, I am aware that the example code contains a major "nono". It just happened to exist in our code and it came to light when test driving Eclipse. I was doing an investigation to whether our project could migrate to Eclipse and this difference seemed to make it risky. I am convinced now that it would not be fair to attribute it to Eclipse.

Thanks to all who replied!

Frans.
 
I agree. Here's the link: http://aspose.com/file-tools
 
subject: Is this a bug in Eclipse's compiler?
 
Similar Threads
Help Requested --> Yep, it's a verifier message!
Strange issue for instance variable.
About contructor in java...
String to Boolean ??
java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType