I've got an interesting problem and cannot understand why it happens
this way.
I have a class to encapsulate the set "waiting" cursor.
public class CursorAction {
public CursorAction() {
Cursor currentCursor = getCursor();
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
execute();
setCursor(currentCursor);
}
protected void execute(){}
}
I use it as follows:
handleForButtonClick() {
new CursorAction() {
protected void execute() {
server.start();
}
};
}
The above code always throws an exception at the line server.start().
The exception is:
AWT-EventQueue-0Exception in thread "AWT-EventQueue-0"
java.lang.NullPointerException
But if I do this then it's always fine:
handleForButtonClick() {
Cursor currentCursor = getCursor();
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
server.start();
setCursor(currentCursor);
}
This is really strange because I believe that the 2 code segments do
the same thing. With my understand about threading in Swing/Awt and
anonymous class, I really don't understand why it happens this way.
Anyone has any idea?
NullBock - 29 Nov 2005 16:42 GMT
I don't think you've posted enough code. What is "server"--it has to
be a field, but of which class?
In any case, it appears that you're running into a problem with a
partially-constructed object. An object is only complete once its
constructor returns, and yet your base class is calling a method, meant
to be overridden, *before* the ctor returns. This is typically
dangerous to do--ctors should almost never call methods that will be
overridden--probably never.
Walter Gildersleeve
Freiburg, Germany
______________________________________________________
http://linkfrog.net
URL Shortening
Free and easy, small and green.
buunguyen@psv.com.vn - 29 Nov 2005 18:19 GMT
Walter, I look at the generated code for the anonymous class and I'm
sure you're right, the 'server' instanced passed to the subclass is not
yet initialized. Thanks for pointing out.
Roedy Green - 29 Nov 2005 17:33 GMT
> server.start();
The key to all this is how and where is server defined. You left
that out.

Signature
Canadian Mind Products, Roedy Green.
http://mindprod.com Java custom programming, consulting and coaching.
Roedy Green - 29 Nov 2005 17:36 GMT
> Cursor currentCursor = getCursor();
> setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
> server.start();
> setCursor(currentCursor);
I presume server.start is not just starting a thread. It is some
action that takes noticeably time and you are willing to freeze the
entire gui during the time it runs.
Normally you would not use a wait cursor, but instead would do that
work on a separate worker thread so that gui could remain responsive.

Signature
Canadian Mind Products, Roedy Green.
http://mindprod.com Java custom programming, consulting and coaching.
buunguyen@psv.com.vn - 29 Nov 2005 17:40 GMT
'server' is an instance field of the class which has the posted code,
it is not a Runnable, the start() simply performs some kind of heavy
calculation.
Thomas Hawtin - 29 Nov 2005 18:40 GMT
> I've got an interesting problem and cannot understand why it happens
> this way.
[quoted text clipped - 5 lines]
> setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
> execute();
It's usually a very bad idea to call overridable methods (on this) from
a constructor.
> setCursor(currentCursor);
> }
[quoted text clipped - 7 lines]
> protected void execute() {
> server.start();
Even System.out.println(server); should NPE!.
System.out.println(MyOuterClass$this); should print "null".
> }
> };
> }
I think I see what is going on. It's a bit subtle. Using -target 1.4
should cure the problems.
When an object is created, the constructor must be called before any
other methods. A constructor must call a constructor of the superclass
before any accesses to this. For the implementation of inner classes,
that means the outer this cannot be set before calling the
superconstructor. In your example the superconstructor calls a method
that uses the subclass outer this, which is at that point null. Oops.
In 1.4 a small change was made to the JVM spec to allow for setting the
outer this before calling the constructor (in most situations). For
backward compatibility javac will only generate code to do this if the
target is set to at least 1.4.
Tom Hawtin

Signature
Unemployed English Java programmer
http://jroller.com/page/tackline/
buunguyen@psv.com.vn - 29 Nov 2005 20:23 GMT
Tom, it works when "-target 1.4". Do you know how it works under the
hood? I look at the decompiled code but I cannot get any sense out of
it.
Thomas Hawtin - 29 Nov 2005 21:07 GMT
> Tom, it works when "-target 1.4". Do you know how it works under the
> hood? I look at the decompiled code but I cannot get any sense out of
> it.
Consider a simplified source file:
class Outer {
{
new Object() { };
}
}
To see what's in the source use javap -c, rather the a to Java source
decompiler. aload_0 refers to this (in a non-static method). aload_1 is
initially the first argument. "<init>" with angles is the internal name
given to all constructors.
The pre 1.4 version:
Compiled from "Outer.java"
final class Outer$1 extends java.lang.Object{
Outer$1(Outer);
Code:
First it calls the superconstructor, this.Object().
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
Now it stores the argument in this.this$0 (i.e. Outer.this).
4: aload_0
5: aload_1
6: putfield #2; //Field this$0:LOuter;
And returns.
9: return
}
The 1.4+ version:
Compiled from "Outer.java"
final class Outer$1 extends java.lang.Object{
final Outer this$0;
Outer$1(Outer);
Code:
First it stores the argument in this.this$0 (i.e. Outer.this), despite
previously not being allowed to access this before calling super. 'this'
is unitialised.
0: aload_0
1: aload_1
2: putfield #1; //Field this$0:LOuter;
Now, with this.this$0 in place, it calls the superconstructor,
this.Object().
5: aload_0
6: invokespecial #2; //Method java/lang/Object."<init>":()V
And returns.
9: return
}
Tom Hawtin

Signature
Unemployed English Java programmer
http://jroller.com/page/tackline/
buunguyen@psv.com.vn - 30 Nov 2005 19:15 GMT
That makes sense. Thanks so much Tom.