33
votes

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
It does look like the terminology has been made needlessly confusing...Jon Skeet
The problem is, we all rely on that terminology to communicate :) With time it seems that we have worked out for ourselves a different terminology, which is more useful.Marko Topolnik
Absolutely. This is one of those cases where I think it makes sense for the spec to change to match the community, rather than vice versa.Jon Skeet
Your final points 1 and 2 are what happens when you remove static rather than when you add it.Ian Roberts
@IanRoberts Thanks, corrected (in the answer, where I have transferred that text).Marko Topolnik

3 Answers

6
votes

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.

0
votes

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

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.