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

3

38

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.

Franglais answered 9/12, 2013 at 10:58 Comment(17)
It does look like the terminology has been made needlessly confusing...Catchpenny
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.Franglais
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.Catchpenny
Your final points 1 and 2 are what happens when you remove static rather than when you add it.Chemurgy
@IanRoberts Thanks, corrected (in the answer, where I have transferred that text).Franglais
I read it like 20 times, and I think the JLS is wrong when it says: it is never static.Alfrediaalfredo
@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.Franglais
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.Edgardoedge
@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.Franglais
@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.Edgardoedge
@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?Franglais
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.)Edgardoedge
@HotLicks My guess would be that they had troubles with implementation, which forced complications in the formalism.Franglais
@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.Edgardoedge
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.Evident
@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.Franglais
Sure. Just wanted to make sure that, if anybody reads that old document, they won't be confused by slight differences in terminology.Evident
F
9

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.

Franglais answered 9/12, 2013 at 11:33 Comment(15)
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.Lizettelizotte
Far from it: you make an essential change to the name resolution semantics, semantics of this, and a number of other subtleties.Franglais
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. ;)Lizettelizotte
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.Franglais
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.Franglais
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.Lizettelizotte
In English too :) They just haven't been chosen with much care.Franglais
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.Lizettelizotte
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!Franglais
What do you mean by "nested class without an enclosing instance"? A local class? Local classes are equal to private inner.Lizettelizotte
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.Franglais
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?Stacistacia
@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.Franglais
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?Stacistacia
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.Franglais
L
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()); } };
}
Lithe answered 9/12, 2013 at 11:19 Comment(1)
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".Franglais
L
-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.

Lizettelizotte answered 9/12, 2013 at 12:18 Comment(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.Franglais
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.Franglais
Basically you are right, but don't carry to the point of absurdity.Lizettelizotte
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.Franglais
@Lizettelizotte It's not absurd, it's correct terminology.Minestrone
The difference is that 'non-static inner' is a contradiction in terms. See JLS 8.1.3 quoted in the question.Tarnopol

© 2022 - 2024 — McMap. All rights reserved.