In Java, how do I access the outer class when I

2019-01-08 15:25发布

问题:

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

回答1:

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


回答2:

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.



回答3:

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.



回答4:

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



回答5:

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.



回答6:

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



回答7:

Where is the problem for simply write this?

class OuterClass{
class InnerClass{
    OuterClass outerClass = OuterClass.this;
}

}



回答8:

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