Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Getting a null "this"

1 view
Skip to first unread message

spam...@gmail.com

unread,
Dec 22, 2005, 5:47:28 PM12/22/05
to
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

unread,
Dec 22, 2005, 5:55:45 PM12/22/05
to
<spam...@gmail.com> wrote:
> 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.

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

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation

Roedy Green

unread,
Dec 22, 2005, 6:03:20 PM12/22/05
to
On 22 Dec 2005 14:47:28 -0800, spam...@gmail.com wrote, quoted or
indirectly quoted someone who said :

>class Outer {
>
> Inner() {
> Outer.this.hello(); // null pointer exception here...
> }
>
> Outer() {
> new Inner(); // because we are in <init> here
> }
>
> void hello() { }
> }

if you intended Inner to be an inner class of outer, you are missing
the keyword class.
--
Canadian Mind Products, Roedy Green.
http://mindprod.com Java custom programming, consulting and coaching.

Benji

unread,
Dec 22, 2005, 6:02:10 PM12/22/05
to
spam...@gmail.com, while chewing on bamboo shoots, wrote:
> 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.

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

John C. Bollinger

unread,
Dec 22, 2005, 10:12:10 PM12/22/05
to
Chris Smith wrote:
> <spam...@gmail.com> wrote:
>
>>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.

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().

--
John Bollinger
jobo...@indiana.edu

Chris Smith

unread,
Dec 22, 2005, 11:09:31 PM12/22/05
to
John C. Bollinger <jobo...@indiana.edu> wrote:
> 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.

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();

Chris Uppal

unread,
Dec 23, 2005, 4:28:28 AM12/23/05
to
John C. Bollinger wrote:

> 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()

Chris Uppal

unread,
Dec 23, 2005, 4:33:27 AM12/23/05
to
Chris Smith wrote:

> 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


Thomas Hawtin

unread,
Dec 23, 2005, 5:55:36 AM12/23/05
to
Chris Uppal wrote:
>
> 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
--
Unemployed English Java programmer
http://jroller.com/page/tackline/

Roedy Green

unread,
Dec 23, 2005, 7:44:00 AM12/23/05
to
On Thu, 22 Dec 2005 21:09:31 -0700, Chris Smith <cds...@twu.net>

wrote, quoted or indirectly quoted someone who said :

>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();
> }
>}

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?

Chris Smith

unread,
Dec 23, 2005, 9:32:06 AM12/23/05
to
Chris Uppal <chris...@metagnostic.REMOVE-THIS.org> wrote:
> 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.

spam...@gmail.com

unread,
Dec 23, 2005, 1:22:26 PM12/23/05
to
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

0 new messages