0

Can nested classes or anonymous classes extend a class or implement an interface in Java? If so, are there any limitations or things I should be aware of? I'm not very familiar with nested classes, will appreciate any examples or clarifications!

class OuterClass {
    class NestedClass implements Runnable {
        @Override
        public void run() {
            System.out.println("Nested class running...");
        }
    }

    public void createInstance() {
        Runnable anonymousRunnable = new Runnable() {
            public void run() {
                System.out.println("Anonymous class running...");
            }
        };

        NestedClass nested = new NestedClass(); 
        nested.run();
        anonymousRunnable.run();
    }
}

Was playing around with these nested/anonymous classes and encountered some errors. Sorry if my code formatting is wrong; not familiar with this site!

3
  • 9
    The code you showed compiles and runs without errors. Doesn't that already answer your question? What exactly are you asking about? Commented Apr 30 at 2:36
  • 5
    If you encountered "some errors," you should tell us what they are. Commented Apr 30 at 2:41
  • 3
    Hello coder12, welcome, the simple answer is “yes”, without having an idea of the application scenario not much more can be added. Commented Apr 30 at 3:29

1 Answer 1

2

As your own snippet shows, method local classes, inner classes, nested classes, and top level classes all have 'definition' lines that look identical, there is room for an extends and implements clause, and you are entirely free to add them.

Method local classes

These are class defs inside a method body:

class TopLevel {
  void methodA() {}
  void foo() {
    int methodVar = 5;
    class MethodLocalClass extends SomeType implements SomeInterface, OtherInterface {
      void innerMethod() {
        methodA();
        System.out.println(methodVar);
      }
    }
  }
}

MethodLocalClasses are exactly like normal classes, except for 2 details:

  • They can call instance methods contained in TopLevel. This is implemented as follows: Instances of your method local class have an invisible final field that is equal to this as resolved at the point you declare it. The compiler compiles that methodA(); line in the above example as $thatSecretField.methodA();. The constructor of MethodLocalClass is similarly secretly upgraded: Even though you have a constructor (because all classes have one; if you fail to type one, the JVM injects YourType() {} for you. And with the same 'you wrote nothing? Then I inject a default' logic, constructors that don't have a super() or this() call inside are compiled as if their first line reads super();. In other words, if you have no constructor at all in your code, then it is identical to having public YourType() { super(); } in your code) - at any rate, you have that constructor, and because it's an inner type, that constructor is silently modified to read public MethodLocalClass(TopLevel $outer) { this.$outer = $outer; super(); }. You can verify all this by using javap -c -v on compiled class files, which shows you the bytecode in human readable memoic names.
  • They also can capture any (effectively) final variables from the method if they are declared and initialized before your method local class definition line. This is done in a similar fashion: With hidden extra fields and upgrading the constructor. That System.out.println(methodVar); works fine and would print 5. Note that methodVar must be final, or must be 'effectively' final which means it could have been marked final without any issue, which is true for the above snippet. That's because the compiler implements all this by making hidden fields and copying the value, which means things would be rather confusing if elsewhere, methodVar is set to '6' but that innerMethod continues to print 5. This confusion is irrelevant if the variable is (effectively) final, hence why it works this way.

Given that they act just like a top level class once you apply these 'inject hidden fields to deal with the outer this and any captured method locals', they can do all the things top level classes can do, including extends and implements.

Inner classes

These are classes declared inside a class and without the modifier static:

class Outer {
  class Inner {}
}

These are exactly like local classes in that they are 'upgraded' with an invisible field containing a reference to an instance of Outer, and you can invoke instance methods of Outer from within Inner. The constructor has a hidden Outer outer$this parameter. You can even invoke the constructor if Inner, passing an explicit Outer:

class Outer {
  class Inner {}

  static void example() {
    new Inner(); // Would be illegal; there is no instance of Outer to pass!
    Outer o = new Outer();
    o.new Inner(); // legal - exotic but legal java syntax!
  }

  void example2() {
    new Inner(); // Legal - there is a `this` available here and the compiler assumes you meant to use that.
  }
}

The same principle applies here - once you've dealt with this hidden field to capture the outer instance business, it's 100% identical to a top level class, and can do all the things they can do.

Nested class

Same as above except with a static modifier. There is absolutely no capturing going on in any way and they are identical to top level classes, completely. The only 'magic' that happens is that they can access private methods of the outer (static or not), but they are compiled as separate classes, so depending on JVM version synthetic package private bridger methods are made to facilitate this (otherwise the JVM would look at the code and reject it due to being invalid, class A cannot access private stuff from class B), or the newer 'nestmates' feature is used, which lets B say: Hey, class verifier? Don't worry if B accesses this private-marked stuff here.

Anonymous classes

That one is a little different, because the syntax is different. Looks like this:

Object o = new Runnable() {
  public void run() { .. }
};

This is just syntax sugar. The syntax is as simple as above - there is no room for the word class nor the word extends or implements. It's just new SomeType(params) { body }. The compiler treats it as if you wrote:

class $SomeRandomlyGeneratedName extends SomeType {
  $SomeRandomlyGeneratedName(String arg0) {
    super(arg0);
  }

  everything-in-body;
}

Object o = new $SomeRandomlyGeneratedName(params);

Because its just syntax sugar, the one and only thing that's interesting about them is what you cannot do from a language syntax perspective, because in execution and compilation terms, just.. apply the above desugaring and now you know exactly what they can and cannot do. This is where we finally get to a 'no, they cannot do that': anonymous inner classes by definition always explicitly extends/implements exactly one type - the one you mentioned as the X in new X() { .. }. But that's all they can extends/implements. That one type. There is simply no place to express any further types.

This isn't limiting in any way; after all, if you want to do that, just manually desugar it. There is nothing an anonymous inner class can do that a method local class cannot do.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.