There has been an interesting discussion on reddit, the other day
Static Inner Classes. When is it too much?
First, let’s review a little bit of basic historic Java knowledge.
Java-the-language offers four levels of nesting classes, and by “Java-the-language”, I mean that these constructs are mere “syntax sugar”. They don’t exist in the JVM, which only knows ordinary classes.
(Static) Nested classes
class Outer {
static class Inner {
}
}
In this case,
Inner
is completely independent of
Outer
, except for a common, shared namespace.
Inner classes
class Outer {
class Inner {
}
}
In this case,
Inner
instances have an implicit reference to their enclosing
Outer
instance. In other words, there can be no
Inner
instance without an associated
Outer
instance.
The Java way of creating such an instance is this:
Outer.Inner yikes = new Outer().new Inner();
What looks totally awkward makes a lot of sense. Think about creating an
Inner
instance somewhere inside of
Outer
:
class Outer {
class Inner {
}
void somewhereInside() {
// We're already in the scope of Outer.
// We don't have to qualify Inner explicitly.
Inner aaahOK;
// This is what we're used to writing.
aaahOK = new Inner();
// As all other locally scoped methods, we can
// access the Inner constructor by
// dereferencing it from "this". We just
// hardly ever write "this"
aaahOK = this.new Inner();
}
}
Note that much like the
public
or
abstract
keywords, the
static
keyword is implicit for nested interfaces. While the following hypothetical syntax might look familiar at first sight…:
class Outer {
<non-static> interface Inner {
default void doSomething() {
Outer.this.doSomething();
}
}
void doSomething() {}
}
… it is not possible to write the above. Apart from the lack of a
<non-static>
keyword, there don’t seem to be any obvious reason why “inner interfaces” shouldn’t be possible. I’d suspect the usual – there must be some really edge-casey caveat related to backwards-compatibility and/or multiple inheritance that prevents this.
Local classes
class Outer {
void somewhereInside() {
class Inner {
}
}
}
Local classes are probably one of the least known features in Java, as there is hardly any use for them. Local classes are named types whose scope extends only to the enclosing method. Obvious use-cases are when you want to reuse such a type several times within that method, e.g. to construct several similar listeners in a JavaFX application.
Anonymous classes
class Outer {
Serializable dummy = new Serializable() {};
}
Anonymous classes are subtypes of another type with only one single instance.
Top 5 Use-Cases For Nested Classes
All of anonymous, local, and inner classes keep a reference to their enclosing instance, if they’re not defined in a static context. This may cause a lot of trouble if you let instances of these classes leak outside of their scope. Read more about that trouble in our article:
Don’t be Clever: The Double Curly Braces Anti Pattern.
Often, however, you do want to profit from that enclosing instance. It can be quite useful to have some sort of “message” object that you can return without disclosing the actual implementation:
class Outer {
// This implementation is private ...
private class Inner implements Message {
@Override
public void getMessage() {
Outer.this.someoneCalledMe();
}
}
// ... but we can return it, being of
// type Message
Message hello() {
return new Inner();
}
void someoneCalledMe() {}
}
With (static) nested classes, however, there is no enclosing scope as the
Inner
instance is completely independent of any
Outer
instance. So what’s the point of using such a nested class, rather than a top-level type?
1. Association with the outer type
If you want to communicate to the whole world, hey, this (inner) type is totally related to this (outer) type, and doesn’t make sense on its own, then you can nest the types. This has been done with
Map
and
Map.Entry
, for instance:
public interface Map<K, V> {
interface Entry<K, V> {
}
}
2. Hiding from the outside of the outer type
If package (default) visibility isn’t enough for your types you can create
private static
classes that are available only to their enclosing type and to all other nested types of the enclosing type. This is really the main use-case for static nested classes.
class Outer {
private static class Inner {
}
}
class Outer2 {
Outer.Inner nope;
}
3. Protected types
This is really a very rare use-case, but sometimes, within a class hierarchy, you need types that you want to make available only to subtypes of a given type. This is a use-case for
protected static
classes:
class Parent {
protected static class OnlySubtypesCanSeeMe {
}
protected OnlySubtypesCanSeeMe someMethod() {
return new OnlySubtypesCanSeeMe();
}
}
class Child extends Parent {
OnlySubtypesCanSeeMe wow = someMethod();
}
4. To emulate modules
Unlike Ceylon, Java doesn’t have first-class modules. With Maven or OSGi, it is possible to add some modular behaviour to Java’s build (Maven) or runtime (OSGi) environments, but if you want to express modules in code, this isn’t really possible.
However, you can establish modules by convention by using static nested classes. Let’s look at the
java.util.stream
package. We could consider it a module, and within this module, we have a couple of “sub-modules”, or groups of types, such as the internal
java.util.stream.Nodes
class, which roughly looks like this:
final class Nodes {
private Nodes() {}
private static abstract class AbstractConcNode {}
static final class ConcNode {
static final class OfInt {}
static final class OfLong {}
}
private static final class FixedNodeBuilder {}
// ...
}
Some of this
Nodes
stuff is available to all of the
java.util.stream
package, so we might say that the way this is written, we have something like:
- a synthetic
java.util.stream.nodes
sub-package, visible only to the java.util.stream
“module”
- a couple of
java.util.stream.nodes.*
types, visible also only to the java.util.stream
“module”
- a couple of “top-level” functions (static methods) in the synthetic
java.util.stream.nodes
package
Looks a lot like Ceylon, to me!
5. Cosmetic reasons
The last bit is rather boring.
Or some may find it interesting. It’s about taste, or ease of writing things. Some classes are just so small and unimportant, it’s just easier to write them inside of another class. Saves you a
.java
file. Why not.
Conclusion
In times of Java 8, thinking about the very old features of Java the language might not prove to be extremely exciting. Static nested classes are a well-understood tool for a couple of niche use-cases.
The takeaway from this article, however, is this. Every time you nest a class, be sure to make it
static
if you don’t absolutely need a reference to the enclosing instance.
You never know when that reference is blowing up your application in production.
Like this:
Like Loading...
“Apart from the lack of a keyword, there don’t seem to be any obvious reason why “inner interfaces” shouldn’t be possible. I’d suspect the usual – there must be some really edge-casey caveat related to backwards-compatibility and/or multiple inheritance that prevents this.”
Not really. Non-static inner interfaces are not possible because, well … how to say it… because they are not possible. What on earth would a non static interface be? Have in every instance of it a reference to an instance of the enclosing class? Actually an interface can do that even if it is static. In every instance it has (it does not actually have any, it is an interface) it has a reference to just anything you like. This way there is no static and non-static inner/nested interfaces. There are just interfaces inside other interfaces or inside classes. Mostly harmless.
Everything is possible, but is it reasonable?
What else would it be? :)
I sense a strong opposition in your words against the thought of this kind of implicit state in interfaces. I share the same opposition, and I don’t think it is needed really. But then again, you gave me no formal reasons why it shouldn’t be possible. An inner interface could impose an enclosing instance as a part of its contract to all implementations. This made no sense at all before default methods. It makes a little more sense now.
What do you mean “impose”? And what do you mean enclosing? An instance enclosing a class/interface? Inner class instances are created by an enclosing instance. Inner classes themselves are loaded by the class loader and are not impose anything on an enclosing instance. Interfaces do not have any instances, that is their nature. Even if there are default methods defined inside.
btw: Groovy does not allow you inner classes only nested ones.
By “impose”, I mean that an inner interface would “impose” a reference to its enclosing instance to all implementors. Example:
If it were possible, the above would be what would happen. If it is useful, I somewhat doubt it.
It makes sense for Groovy not to support all of this inner class mess. Keeps things simple, and more concise as people won’t ask silly questions as I did.