32

If I have an instance of an inner class, how can I access the outer class from code that is not in the inner class? I know that within the inner class, I can use Outer.this to get the outer class, but I can't find any external way of getting this.

For example:

public class Outer {
  public static void foo(Inner inner) {
    //Question: How could I write the following line without
    //  having to create the getOuter() method?
    System.out.println("The outer class is: " + inner.getOuter());
  }
  public class Inner {
    public Outer getOuter() { return Outer.this; }
  }
}
Kip
  • 107,154
  • 87
  • 232
  • 265
  • could you explain what problem you are solving? Or is this academic? – Thorbjørn Ravn Andersen Apr 18 '09 at 18:59
  • Academic, mainly. I ran across it when coming up with an answer to this: http://stackoverflow.com/questions/763359/validating-instances-of-inner-classes/763504 I gave two answers, one doing what the OP was asking for using the same workaround I use above (a getOuter() method). But in my other answer (the one that has been upvoted) I said to design it a different way so that this wasn't necessary. But I was still curious if it was possible to do this. – Kip Apr 18 '09 at 20:43
  • 1
    Your provided example is the best answer to this question. – Pindatjuh Jun 14 '10 at 14:27

8 Answers8

33

The bytecode of the Outer$Inner class will contain a package-scoped field named this$0 of type Outer. That's how non-static inner classes are implemented in Java, because at bytecode level there is no concept of an inner class.

You should be able to read that field using reflection, if you really want to. I have never had any need to do it, so it would be best for you to change the design so that it's not needed.

Here is how your example code would look like when using reflection. Man, that's ugly. ;)

public class Outer {
    public static void foo(Inner inner) {
        try {
            Field this$0 = inner.getClass().getDeclaredField("this$0");
            Outer outer = (Outer) this$0.get(inner);
            System.out.println("The outer class is: " + outer);

        } catch (NoSuchFieldException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    public class Inner {
    }

    public void callFoo() {
        // The constructor of Inner must be called in 
        // non-static context, inside Outer.
        foo(new Inner()); 
    }

    public static void main(String[] args) {
        new Outer().callFoo();
    }
}
Esko Luontola
  • 73,184
  • 17
  • 117
  • 128
  • 2
    That's (IIRC) compiler dependent, and you'll need a setAccessible in general. – Tom Hawtin - tackline Apr 20 '09 at 12:32
  • I think (too lazy to look up the spec) even the naming of that field is compiler dependent, so you *could* possibly run into problem if you try that hack with another compiler. – Joachim Sauer Nov 03 '09 at 23:05
  • I am reading Java Concurrency in Practice, and one of the examples showed how the this reference can escape with the inner class, if we pass an instance of the inner class to some alien code in constructor. And I am trying to write the alien code that can actually get a hold on the reference before the constructor returns. Thanks very much for the answer. – xiaofeng.li May 02 '16 at 00:33
  • 2
    Does not work when there are several levels of nesting. Because the field can have name `this$1` in that case. – Jan Schaefer Dec 26 '16 at 15:33
14

There is no way, by design. If you need to access the outer class through an instance of the inner one, then your design is backwards: the point of inner classes is generally to be used only within the outer class, or through an interface.

Michael Borgwardt
  • 342,105
  • 78
  • 482
  • 720
  • 2
    That was my thought too, but I can't seem to find any documentation which addresses this limitation. If you can find anything official I'll gladly accept this answer. – Kip Apr 18 '09 at 20:45
  • It's not an explicit "limitation" - why should there be documentation about the ABSENCE of a particular (rather obscure) feature? – Michael Borgwardt Apr 18 '09 at 21:12
  • 2
    15.8.3 and 15.8.4 covers it. "Any lexically enclosing instance can be referred to by explicitly qualifying the keyword this." In the question's case, it isn't a lexically enclosing instance. – Tom Hawtin - tackline Apr 20 '09 at 12:40
  • What about accessing it from JNI i.e. through a `native` method of `Inner` class? – Berkus Aug 22 '17 at 12:38
8

What's wrong with adding a getter when you need to access the outer class? That way you can control whether access is allowed or not.

starblue
  • 55,348
  • 14
  • 97
  • 151
6

This, in fact, is a very good question if, for example, you need to be able to check if two distinct instances of an Innner class share the same instance of Outer class (== or equals depending on the context).

I'd suggest to make a general purpose interface (not absolutely required for named inner classes but can be "instancedof"/casted to):

public interface InnerClass<Outer> {
    Outer getOuter();
}

that can be applied to any named inner class.

Then you do something like:

class MyInnerClass implements InnerClass<Outer> {
    Outer getOuter() {
        return Outer.this;
    }
    // remaining implementation details
}

and this way you can access outer class from any inner class implementing InnerClass<Outer> interface (and check it actually implements it).

If your inner class is anonymous, you can only do (thanks to Rich MacDonald for its sample):

new InterfaceOrAbstractClass<Outer>() {
    Outer getOuter() { // super inefficient but this is the only way !
        return (Outer)getClass().getDeclaredField("this$0");
    }
    /* other methods */
}

but InterfaceOrAbstractClass must implements InnerClass<Outer> to be able to access getOuter() outside of the anonymous class body!

It would be so much easier if javac automatically implemented some kind of InnerClass<Outer> interface on ALL inner classes, and it could do that super efficiently even on anonymous classes (no sluggish introspection processing)!

1

Simply write this

class Outer {
  class Inner {
    Outer outer = Outer.this;
  }
}
lue
  • 449
  • 5
  • 16
Igor
  • 19
  • 1
  • 4
    The way this is written, it's hard to tell if it's an answer or a new question. – Cat Jan 29 '13 at 17:39
1

Here is one reason you may want this behavior: You have the inner class instance and need to access a method defined in the outer class using reflection.

For the record, inner.getClass().getDeclaredField("this$0") works. Since the field is private, you also need to call field.setAccessible(true).

lue
  • 449
  • 5
  • 16
1

If you've got a (non-static) inner class, then you're by definition working with something that only functions inside the enclosing context of the outer class. So to get a handle to the inner class, you'd already have to have retrieved it through the outer instance. So the only way I can see to get to the point where you'd need an accessor like that is to have grabbed the inner through the outer ref and then lost or discarded the outer instance reference.

For example:

public class Outer{ 
   public class Inner {
   }
}

public class UsesInner{
 Collection<Outer.Inner> c = new ArrayList<Outer.Inner>();
}

Now the only way you can populate c is by creating Outer() instances. I'd be curious to see a useful purpose for something like this.

Steve B.
  • 55,454
  • 12
  • 93
  • 132
  • Honestly, I can't think of a good reason either. I just ran across it trying to answer this question: http://stackoverflow.com/questions/763359/validating-instances-of-inner-classes/763504 In that case, he wanted to make sure he had an inner class of a specific instance of the outer class. I suggested in another answer that he should just define the method in the inner class so that it wouldn't be a problem. But, again, I was curious if there way to do it. – Kip Apr 18 '09 at 17:04
0

Can't you just do something like this:

public static class Inner { // static now
    private final Outer parent;

    public Inner(Outer parent) { this.parent = parent; }

    public Outer get Outer() { return parent; }
}
Tim Frey
  • 9,901
  • 9
  • 44
  • 60