Is it true that every inner class requires an enclosing instance?

7.6k views Asked by At

The term inner class is conventionally taken to mean "a nested class which requires an enclosing instance". However, the JLS states as follows:

8.1.3. Inner Classes and Enclosing Instances

[...]

Inner classes include local (§14.3), anonymous (§15.9.5) and non-static member classes (§8.5).

[...]

An instance of an inner class whose declaration occurs in a static context has no lexically enclosing instances.

Also,

15.9.5. Anonymous Class Declarations

[...]

An anonymous class is always an inner class (§8.1.3); it is never static (§8.1.1, §8.5.1).

And it is well-known that an anonymous class may be declared in a static context:

class A {
  int t() { return 1; }
  static A a =  new A() { int t() { return 2; } };
}

To describe it poignantly,

new A() {} is a nested class without an enclosing instance, defined in a static context, but it is not a static nested class—it is an inner class.

Are we all assigning inappropriate meanings to these terms in day-to-day usage?

As a related point of interest, this historical specification document defines the term top-level as the opposite of inner:

Classes which are static class members and classes which are package members are both called top-level classes. They differ from inner classes in that a top-level class can make direct use only of its own instance variables.

Whereas in the common usage top-level is taken to be the opposite of nested.

3

There are 3 answers

15
Marko Topolnik On BEST ANSWER

The distinctions laid out in the question make perfect sense from the specification's standpoint:

  • an inner class has restrictions applied to it, which have nothing to do with the question of enclosing instances (it may not have static members, for example);

  • the concept of a static nested class is basically just about namespacing; these classes might rightfully be termed top-level, together with what we usually assume as top-level classes.

It just so happens that removing static from a nested class declaration does two separate things at once:

  1. it makes the class require an enclosing instance;
  2. it makes the class inner.

We rarely think about inner as entailing restrictions; we only focus on the enclosing instance concern, which is much more visible. However, from the specification's viewpoint, the restrictions are a vital concern.

What we are missing is a term for a class requiring an enclosing instance. There is no such term defined by the JLS, so we have (unaware, it seems) hijacked a related, but in fact essentially different, term to mean that.

1
MrBackend On

Well, doesn't the anonymous class have an enclosing instance in your case as well? It is the reference that's static, not the instance of the anonymous class. Consider:

class A {
   int t() { return 1; }
   static A a = new A() { { System.out.println(t()); } };
}
6
Mikhail On

There is no difference between static inner class and no static. i don't understand why they should be considered separately. Have a look at the following code:

public class Outer {
    public static class StaticInner{
        final Outer parent;

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

    public static void main(String[] args) {
        new StaticInner(new Outer());
        new Outer().new Inner();
    }
}

And then at StaticInner and Inner classes bytecode:

public class so.Outer$Inner extends java.lang.Object{
final so.Outer this$0;
public so.Outer$Inner(so.Outer);
  Code:
   0:   aload_0
   1:   aload_1
   2:   putfield        #1; //Field this$0:Lso/Outer;
   5:   aload_0
   6:   invokespecial   #2; //Method java/lang/Object."<init>":()V
   9:   return

}

public class so.Outer$StaticInner extends java.lang.Object{
final so.Outer parent;
public so.Outer$StaticInner(so.Outer);
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   aload_0
   5:   aload_1
   6:   putfield        #2; //Field parent:Lso/Outer;
   9:   return
}

Actually there is no difference between them at all. I'd say non-static inner class is just a syntactic sugar. A shorter way to write a common thing, no more. The only slight difference is that in no-static inner class, reference to the enclosing class is assigned before calling parent constructor, this might affect some logic, but I don't think that it's so much critical, to consider them separately.

P.S. One more question on a related topic, which might be interesting.