zvrba
zvrba

Reputation: 24546

How to make an outer class inherited from an inner class?

How can I make something like this work:

class Outer {
  int some_member;

  abstract class InnerBase {
    abstract void method();
  }
}

class OuterExtendsInner extends Outer.InnerBase {
  OuterExtendsInner(Outer o) { o.super(); }
  void method() {
    // How do I use some_member here?
    // Writing Outer.this.some_member -> error about Outer not being an enclosing class
    // Writing just some_member -> no-go, either
  }
}

The workaround is to have a method in InnerBase that returns Outer.this and call THAT from derived classes, but is there another way?

I primarily want to extend the InnerBase from outside in order to have better code-organization, but I could move all derived classes into Outer.

Upvotes: 8

Views: 1585

Answers (7)

Acn
Acn

Reputation: 1066

The outer class can extend the inner class iff the inner class is compiled to ".class".

Now, every time you compile the outer class it encounters the "extends innerclass" which is

not yet compiled and the compiler will throw a NoClassDefException or ClassNotFoundException.

Isn't it ? So you will never get that inner class compiled. If you can overcome this problem

then you can also extend the inner class :) .

Upvotes: 0

00JT
00JT

Reputation: 11

Just have a getter method in the InnerBase?

class Outer {
  int some_member;

  abstract class InnerBase {
    abstract void method();
    protected int getSome_Member() // This is possible, because Abstract classes can have non-abstract methods.
    {
       return some_member;
    }
  }
}

class OuterExtendsInner extends Outer.InnerBase {
  OuterExtendsInner(Outer o) { o.super(); }
  void method() {
       // you can access "some_member" now
      int myNumber = getSome_Member();

  }
}

Upvotes: 1

Don Roby
Don Roby

Reputation: 41137

I haven't tried WhiteFang34's answer. It might work, but I'm not clear on it ...

If you really want to define an extension of your inner class elsewhere than in the outer class, the most natural thing would be to define it as an extension of the inner class in another outer extending your outer class as follows:

class Outer {
  int some_member;

  abstract class InnerBase {
    abstract void method();
  }
}

class OuterExtendsOuter extends Outer {
  class InnerExtendsInner extends Outer.InnerBase {
    void method() {
       System.out.println(some_member);
    }
  }
}

I haven't actually run this code either, but it should work.

Update:

Based on the comment thread, I have now compiled and run both my code above and WhiteFang34's code.

Both in fact work, but as noted in the comments by Paŭlo Ebermann, both create two copies of the outer inside the instantiated inner class.

I'm going to upvote Paŭlo's answer, and would advocate just not trying to do this by either tactic, as it's really an abuse of the inner class mechanism.

Just make your extended inner classes live inside the same outer class!

Update 2:

What happens in my code, based on runtime examination using a debugger and on examining the output from javap inspections of the classes, is that both InnerBase and OuterExtendsOuter$InnerExtendsInner have synthetic private final fields named this$0. Because no constructors are explicitly defined, the default constructors are used, and the code snippet

    OuterExtendsOuter outer = new OuterExtendsOuter();
    Outer.InnerBase inner = outer.new InnerExtendsInner();

causes these two fields to both reference outer.

In other words, Paŭlo's comment is entirely correct.

By further experimentation, the same actually happens if you extend InnerBase in another inner class of Outer, so it has little to do with it being defined in the same outer class or an extension of it, but is in fact an outcome of how non-static inner classes are handled generally.

I suspect this is documented somewhere, but I haven't seen that.

Probably best to mix inheritance and inner classes as little as possible!

Upvotes: 1

Paŭlo Ebermann
Paŭlo Ebermann

Reputation: 74750

The problem here is that the synthetic field which links InnerBase to Outer is a private field. Thus, we can only access the outer object from within InnerBase, or if some method or field there provides a reference to the same object.

Upvotes: 3

The answer is: you can't, because it would break encapsulation. Only InnerBase can have access to attributes of Outer, not OuterExtendsInner. It is not direct inheritance. InnerBase does not inherit of Outer.

Upvotes: 1

jmg
jmg

Reputation: 7414

Well your problem is that every instance of InnerBase (I know it's abstract) has to have a reference to an Outer object. That is part of the semantics of nested classes. Instantiating OuterExtendsInner would need such a reference. You can avoid that making InnerBase a static nested class.

Upvotes: 0

WhiteFang34
WhiteFang34

Reputation: 72039

You could do this in OuterExtendsInner:

class OuterExtendsInner extends Outer.InnerBase {
    Outer o;

    OuterExtendsInner(Outer o) {
        o.super();
        this.o = o;
    }

    void method() {
        // now you can reference o.some_member
        int x = o.some_member;
    }
}

Upvotes: 3

Related Questions