Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsWhite Papers
Discussion GroupsFirst AidDatabasesJavaBeansGUIJava 3DVirtual MachineCORBASecurityToolsGeneral
Java DirectoryOpen Source ProjectsSample Book ChaptersUser GroupsWeb Resources
Related Topics
Databases.NETMore Topics ...

Java Forum / General / December 2005

Tip: Looking for answers? Try searching our database.

Getting a null "this"

Thread view: 
spamwalsh@gmail.com - 22 Dec 2005 22:47 GMT
This is mostly a sanity check. In the jdk I am using, it appears that
inner classes see a null outer "this" pointer if the inner object is
constructed during the outer <init>. Is this even remotely reasonable?

-Kevin

Code is something like this:

class Outer {

   Inner() {
     Outer.this.hello(); // null pointer exception here...
   }

 Outer() {
   new Inner(); // because we are in <init> here
 }

 void hello() { }
}
Chris Smith - 22 Dec 2005 22:55 GMT
> This is mostly a sanity check. In the jdk I am using, it appears that
> inner classes see a null outer "this" pointer if the inner object is
> constructed during the outer <init>. Is this even remotely reasonable?

Yes.  If you are calling non-private and non-final methods from a
constructor, then it's very likely that you'll end up observing
uninitialized fields because you reach certain code before you reach the
instance initializer or constructor that initializes that.  An inner
class's implicit outer class point (Outer.this) is just another
reference from the VM's standpoint, and it will be initialized in or
just prior to the constructor, just like anything else.

> Code is something like this:

That code doesn't even compile.  If you post real code, I could verify
that you're seeing what I think you are.

Signature

www.designacourse.com
The Easiest Way To Train Anyone... Anywhere.

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation

John C. Bollinger - 23 Dec 2005 03:12 GMT
>>This is mostly a sanity check. In the jdk I am using, it appears that
>>inner classes see a null outer "this" pointer if the inner object is
[quoted text clipped - 7 lines]
> reference from the VM's standpoint, and it will be initialized in or
> just prior to the constructor, just like anything else.

I'd like to see the real code.  My best guess as to what he meant does
not exhibit the behavior he described.  Here's my version of the code:

class Outer {

    class Inner {
        public Inner() {
            Outer.this.hello();
        }
    }

    public Outer() {
        new Inner();
    }

    void hello() {
        System.out.println("hello");
    }

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

When I run it (Sun JRE 1.5.0_02), it prints "hello" and exits normally,
as I expected it would.

When a constructor or instance initializer runs, it has access to a
valid "this" reference (per JLS).  I haven't found the same level of
assurance in the JLS for a qualified "this", but I do find a flat "Any
lexically enclosing instance can be referred to by explicitly qualifying
the keyword this" (JLS 15.8.4).  There is no particular reason why the
qualified "this" reference(s) should be unavailable on account of inner
class instantiation happening in a constructor.  In fact, I assert that
the Java compiler is in error in successfully compiling the above code
if it cannot or does not ensure that the Inner constructor has a valid
value for Outer.this.

It is an *entirely* different story, however, if the inner class'
constructor assumes that its containing instance is fully initialized,
and thus attempts to directly or indirectly read or manipulate its
member variables.  For instance:

class Outer2 {

    PrintStream out;

    class Inner2 {
        public Inner2() {
            Outer2.this.hello();
        }
    }

    public Outer2() {
        new Inner2();
        out = System.out;
    }

    void hello() {
        out.println("hello");
    }

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

This version of the program quite expectedly throws a
NullPointerException from Outer2.hello().

Signature

John Bollinger
jobollin@indiana.edu

Chris Smith - 23 Dec 2005 04:09 GMT
> When a constructor or instance initializer runs, it has access to a
> valid "this" reference (per JLS).  I haven't found the same level of
[quoted text clipped - 3 lines]
> qualified "this" reference(s) should be unavailable on account of inner
> class instantiation happening in a constructor.

I agree that:

1. The JLS is at best unclear, and at worst fails to match the
implementation, on whether a qualified this reference is guaranteed to
point to an object at all times.

2. The mere creation of an inner class instance from a constructor is
not sufficient to cause the problem.

Here's some code that, correctly or not, does manage to observe a null
qualified this pointer from an inner class in Java.

class Outer
{
   public Outer()
   {
       test();
   }

   public void test()
   {
   }

   public class Inner extends Outer
   {
       public void test()
       {
           System.out.println(Outer.this);
       }
   }

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

Signature

www.designacourse.com
The Easiest Way To Train Anyone... Anywhere.

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation

Chris Uppal - 23 Dec 2005 09:33 GMT
> Here's some code that, correctly or not, does manage to observe a null
> qualified this pointer from an inner class in Java.
>
> class Outer
> {
[...]
>     public class Inner extends Outer
>     {

FWIW, this would not be the first bug to surface in connection with inner
classes that extent their outers.

>     public static void main(String[] args)
>     {
>         new Inner();
>     }

I'm surprised that compiles.  It seems an obvious semantic error.  And, in
fact, it doesn't compile for me; which compiler are you using  (I'm using the
javac from 1.5.0-b64) ?

   -- chris
Chris Smith - 23 Dec 2005 14:32 GMT
> I'm surprised that compiles.  It seems an obvious semantic error.  And, in
> fact, it doesn't compile for me; which compiler are you using  (I'm using the
> javac from 1.5.0-b64) ?

Sorry, I was in a hurry.  Let me try this again, and compile it this
time.

Here's what I meant to write:

abstract class Base
{
   public Base()
   {
       test();
   }

   public abstract void test();
}

public class Outer
{
   public class Inner extends Base
   {
       @Override
       public void test()
       {
           System.out.print(Outer.this);
       }
   }

   public static void main(String[] args)
   {
       new Outer().new Inner();
   }
}

However, it doesn't behave as I expected.  Your other post explains
this; apparently, this was fixed as a bug in 1.4?  I must have missed
that change.

Signature

www.designacourse.com
The Easiest Way To Train Anyone... Anywhere.

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation

Roedy Green - 23 Dec 2005 12:44 GMT
>class Outer
>{
[quoted text clipped - 20 lines]
>    }
>}

Since Inner in an inner class of Outer, you should not be allowed to
instantiate it except in an instance method of Outer. That is a
compiler bug, is it not?

Signature

Canadian Mind Products, Roedy Green.
http://mindprod.com Java custom programming, consulting and coaching.

Chris Uppal - 23 Dec 2005 09:28 GMT
> In fact, I assert that
> the Java compiler is in error in successfully compiling the above code
> if it cannot or does not ensure that the Inner constructor has a valid
> value for Outer.this.

I agree, but it hasn't always been able to ensure that.  The code it generates
these days was disallowed by pre-1.4 verifiers (because it assigns the
synthetic $outer field before calling the superclass constructor, which is
normally illegal).  So the following code will print hello if compiled with:

   javac -g -source 1.4 -target 1.4  Outer.java

but will NPE if compiled with:

   javac -g -source 1.3 -target 1.3  Outer.java

It may be that the OP is in a 1.3-based environment ?

   -- chris

===== example code =======

abstract class Helper
{
Helper()
{
 doit();
}

abstract void doit();
}

class Outer
{
    class Inner
    extends Helper
    {
        void
        doit()
        {
            Outer.this.hello();
        }
    }

    public Outer()
    {
        new Inner();
    }

    void
    hello()
    {
        System.out.println("hello");
    }

    public static void
    main(String[] args)
    {
        new Outer();
    }
}
Thomas Hawtin - 23 Dec 2005 10:45 GMT
> It may be that the OP is in a 1.3-based environment ?

Or, perhaps more likely, just accepting the (badly chosen) default
-target in 1.4.

Tom Hawtin
Signature

Unemployed English Java programmer
http://jroller.com/page/tackline/

Benji - 22 Dec 2005 23:02 GMT
> This is mostly a sanity check. In the jdk I am using, it appears that
> inner classes see a null outer "this" pointer if the inner object is
> constructed during the outer <init>. Is this even remotely reasonable?

Yes, this is the correct behavior.  Similar to the fact that you can't call
methods on the superclass before the superconstructor is done, you can't
access the outer class until its constructor is done running.

Signature

Of making better designs there is no end,
 and much refactoring wearies the body.

spamwalsh@gmail.com - 23 Dec 2005 18:22 GMT
Yup, several of you were right (Chris S., Benji and others). I realized
just after I posted what the problem was, too, but by that time I was
on the bus on the way home. My actual code has one additional twist:
the "Outer.this" reference gets stuck with the null pointer
permanently. The constructor is actually _passed_ the null pointer as
if it were the outer reference.

The actual code I was using is close to the following:

class Outer {
    public static void main(String args[]) {
        new Outer();
    }

    Outer() {
        Inner inner = (Inner)(new InnerFactory()).o;
        System.out.println("still " + inner.parent());
    }

    class Inner {
        Inner() { System.out.println(Outer.this); }
        Outer parent() { return Outer.this; }
    }

    class InnerFactory extends Factory {
        Object makeone() { return new Inner(); }
    }

}

class Factory {
    Object o;
    Factory()  { o = makeone(); }
    Object makeone() { return "unused"; }
}

Compiled and run with sun's 1.4.1_02 jdk, I get:
null
still null

The explanation probably looks something like this:

- main()
-- Outer.<init>()
--- Factory.<init>() // superconstructor of InnerFactory
---- InnerFactory.makeone() // has null Outer.this, since constructor
not yet called
----- Inner.<init>() // passed the null Outer.this by
InnerFactory.makeone()
------ println()      // prints the null Outer.this
----- return from Inner.<init>()
---- return from InnerFactory.makeone()
--- InnerFactory.<init>() // finished superconstructor, start
InnerFactory constructor
---- // now sets Outer.this in InnerFactory instance, but too late
--- return...

Notice how the inner object is now wedged, since it was passed a null
pointer and stored that as its Outer.this reference. Even after the
constructors are all done, the inner object's Outer.this reference is
permanently set to null. Ouch. I don't have the time or motivation to
figure
out if this is legit behavior or what, but maybe I deserved it for such
ugly
code.

Thanks for the comments!

-Kevin
Roedy Green - 22 Dec 2005 23:03 GMT
>class Outer {
>
[quoted text clipped - 8 lines]
>  void hello() { }
> }

if you intended Inner to be an inner class of outer, you are missing
the keyword class.
Signature

Canadian Mind Products, Roedy Green.
http://mindprod.com Java custom programming, consulting and coaching.



Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2009 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.