35

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.

Marko Topolnik
  • 195,646
  • 29
  • 319
  • 436
  • 8
    It does look like the terminology has been made needlessly confusing... – Jon Skeet Dec 09 '13 at 11:08
  • 3
    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 Dec 09 '13 at 11:10
  • 4
    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 Dec 09 '13 at 11:11
  • Your final points 1 and 2 are what happens when you _remove_ `static` rather than when you _add_ it. – Ian Roberts Dec 09 '13 at 11:32
  • @IanRoberts Thanks, corrected (in the answer, where I have transferred that text). – Marko Topolnik Dec 09 '13 at 11:35
  • I read it like 20 times, and I think the JLS is wrong when it says: *it is never `static`*. – Martijn Courteaux Dec 09 '13 at 12:35
  • @MartijnCourteaux You just have to adopt the very special meaning of `static` in that sentence: in essence, "never `static`" just restates "always inner" because they are each other's opposites (within the realm of nested classes). `static` nested classes do not have the restrictions of inner classes and they can be considered "top-level" classes. – Marko Topolnik Dec 09 '13 at 12:42
  • The term "lexically" here is tricky. In compiler-speak it refers to addressability, not physical position. But the meaning of "whose declaration occurs in a static context" is hard to grok -- read at face value it contradicts reality. – Hot Licks Dec 09 '13 at 12:59
  • @HotLicks It contradicts reality only because we are using *inner* in a sense which has little to do with its JLS meaning. Re: "lexically", shouldn't that be unequivocally related to occurring within the enclosing construct's branch of the parse tree? Because that's what it means when related to the concept of closures. – Marko Topolnik Dec 09 '13 at 13:06
  • @MarkoTopolnik - Conventionally, lexical scope refers to name/reference scope, which is not *necessarily* related to physical position in the source code. The distinction is not so obvious in the C-based languages, but there are some slightly bizarre scenarios in Algol, Pascal, et al. – Hot Licks Dec 09 '13 at 16:45
  • @HotLicks Now that you've worked it up, you'll have to give me an example :) Can you post a short snippet, or a link to such a snippet, where the non-obvious lexical scope can be seen? – Marko Topolnik Dec 09 '13 at 18:23
  • It's been about 20 years since I last mucked with Pascal, and 30 since I played with Algol, so I can't really recall any details. I'm vaguely remembering that Algol (at least some versions) had a very crude closure-like mechanism, inherited from COBOL, where the scoping rules were incomprehensible. (Algol, of course, "invented" lexical scope.) – Hot Licks Dec 09 '13 at 19:29
  • @HotLicks My guess would be that they had troubles with implementation, which forced complications in the formalism. – Marko Topolnik Dec 09 '13 at 19:54
  • @MarkoTopolnik - Yep, closures are complex enough when you know what you're doing. Back then the concept of a callable procedure was still kind of controversial. Pascal, IIRC, is a bit easier to understand, but there are still some cases that make you say "Whoa!!" when you first see them. In particular, calling from within a nested scope to a procedure in an outer scope, and then back into an inner scope. – Hot Licks Dec 09 '13 at 20:14
  • The *Inner Classes Specification* document is from 1997 and hasn't been kept up to date. I would say that where it contradicts the JLS, the JLS should take precedence. – Stuart Marks Dec 10 '13 at 00:16
  • @StuartMarks I am not entertaining any contradictions---the historical document is here just to show what they meant by "top-level class", a term which doesn't exist in the JLS. – Marko Topolnik Dec 10 '13 at 07:10
  • Sure. Just wanted to make sure that, if anybody reads that old document, they won't be confused by slight differences in terminology. – Stuart Marks Dec 10 '13 at 08:37

3 Answers3

8

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.

Marko Topolnik
  • 195,646
  • 29
  • 319
  • 436
  • By removing word `static`, you just add an implicit constructor to the class. Why does it make so much sense? You can always create exactly the same constructor explicitly. – Mikhail Dec 09 '13 at 12:40
  • Far from it: you make an essential change to the name resolution semantics, semantics of `this`, and a number of other subtleties. – Marko Topolnik Dec 09 '13 at 12:45
  • If you know how things work under the hood, they are all the same. These concepts might really look distinct only to newbys, who suffer from a lack of understanding. I don't mean you. ;) – Mikhail Dec 09 '13 at 12:51
  • I don't get your point: do you say that the feature of an enclosing instance is overall irrelevant to the language? If not, then the point is not in those things which remain the same, but in those which change. – Marko Topolnik Dec 09 '13 at 12:53
  • 1
    On a related note, it would make a *huge* difference to me if Java had a feature which relieved me of writing getters, setters, `equals`, `hashCode`, and `toString` ever again---at least when my intended implementation is the well-understood default. – Marko Topolnik Dec 09 '13 at 12:57
  • There is difference between these concepts, but very slight. May be this is because of language. In Russian inner and nested are very close synonyms. – Mikhail Dec 09 '13 at 13:05
  • In English too :) They just haven't been chosen with much care. – Marko Topolnik Dec 09 '13 at 13:07
  • 1
    To me static and non-static is much more understandable then inner and nested. They define things much clearer. Reading JLS with inner/nested terminology would be a pain. – Mikhail Dec 09 '13 at 13:18
  • 1
    If by "non-static" you mean "not having an enclosing instance", then your distinction doesn't work---that's the point if this question :) There is such a thing as a *nested class without an enclosing instance*, which is nevertheless *non-static*! – Marko Topolnik Dec 09 '13 at 13:20
  • What do you mean by "nested class without an enclosing instance"? A local class? Local classes are equal to private inner. – Mikhail Dec 09 '13 at 13:27
  • 1
    The concept of "local class" has again nothing to do with enclosing instances. Some have them, some don't. In your answer, `StaticInner` is an example of a nested class without an enclosing instance. – Marko Topolnik Dec 09 '13 at 13:30
  • It definitely seems the static qualifier for inner class (which I now simply infer to denote a non-top-level class) was poorly thought out, though don't static and non-static already serve to distinguish whether or not a class requires an enclosing instance? Why do we need a new term? – Drazen Bjelovuk Jan 29 '16 at 22:55
  • @drazen non-top-level class is called "nested"; inner class is a non-static nested class; an inner class may or may not require an enclosing instance, this depends on whether it's declared in a static context or not. – Marko Topolnik Jan 29 '16 at 23:19
  • If static member classes are nested, then surely nested and top-level are not mutually exclusive? And isn't the requirement of an enclosing instance central to the characterization of non-static? – Drazen Bjelovuk Jan 29 '16 at 23:53
  • 1. "Top-level" is not a term defined by the JLS. 2. Enclosing instance is a _sufficient_, but not a _necessary_ condition for a class to belong to the _inner_ category. – Marko Topolnik Jan 30 '16 at 06:25
0

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()); } };
}
MrBackend
  • 597
  • 3
  • 15
  • 2
    Do note the distinction between `this`, also termed the *zeroth enclosing instance*, and the *first enclosing instance* which I am takling about, which is referred to by the shortcut term "enclosing instance". – Marko Topolnik Dec 09 '13 at 11:28
-2

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.

Community
  • 1
  • 1
Mikhail
  • 4,175
  • 15
  • 31
  • 6
    What you call `StaticInner` is not at all an inner class. It is *nested*, but that is beside the point of my question. And yes, the difference is visible only at the source code level, in the rules enforced by the compiler. The bytecode is the same because that was the design requirement when inner classes were introduced in Java 1.1. – Marko Topolnik Dec 09 '13 at 12:28
  • 3
    The syntactical consequences of a class with an enclosing instance are not at all a trifle. If you consider every syntactical feature as immaterial, then all Turing-complete languages are the same in your eyes. For example, Assembler is on an equal footing with Haskell. – Marko Topolnik Dec 09 '13 at 12:40
  • Basically you are right, but don't carry to the point of absurdity. – Mikhail Dec 09 '13 at 12:44
  • 4
    Of course, introducing an absurd clause is just an instrument to help make my point. I am just pointing out how far that particular standpoint can be taken, if taken literally. – Marko Topolnik Dec 09 '13 at 12:46
  • 2
    @Mikhail It's not absurd, it's correct terminology. – Jossie Calderon Jun 12 '16 at 18:09
  • The difference is that 'non-static inner' is a contradiction in terms. See JLS 8.1.3 quoted in the question. – user207421 Apr 29 '22 at 00:45