Note: in this page, "inner classes" means "non-static inner classes".
JavaIdiom: Inner classes should never be exposed in the PublicInterface of a class, because they make the interface ugly and inhibit refactorings later.
This is an example of an inner class:
package com.c2.foo; public class Outer { int x, y; public Outer(int x, int y) { this.x = x; this.y = y; } public int getBigger() { return (x > y) ? x : y; } // ... more methods & constructors public class Inner { int a, b; public Inner(int a) { this.a = a; this.b = Outer.this.x * 2; } // ... more methods and constructors } }This does several things:
public class Parent { int x, y; } public class Child { int a, b; private final Parent parent; // explicitly stored public Child(Parent parent, int a) { this.parent = parent; this.a = a; this.b = parent.x * 2; } }Relatively few Java programmers, including HighlyPaidConsultants, really understand how inner classes work, but they get by (mostly) by copying from the examples.
It's arguable whether inner classes are a kludge or not -- certainly they are convenient for many situations, but other languages have more consistent and equally flexible solutions.
In any case, inner classes should never be exposed in the public interface of the package. Here's why:
Outer o = getSomeOuter(); Outer.Inner i = o.new Inner(3); // ...
public interface CareTaker? { ... } public class Parent implements CareTaker? { ... } public class Child { // ... private final CareTaker? careTaker; public Child(CareTaker? careTaker, int x) { this.careTaker = careTaker; // ... } }Inner classes have a perfectly appropriate place as SyntacticSugar for private implementations of public interfaces, but they should not be abused. Nested classes (aka static inner classes) and interfaces are less harmful, but still should be used in moderation.
Actually, a fair number of people understand inner classes. They're not that complicated an idea.
The two final bullet points aren't parallel. "Bizarre and ugly" seems needlessly pejorative. And saying that public inner classes limit refactoring is confusing. More so than using a second "outer class" (e.g. another class, defined in a second .java file)? Of course not.
So; we have two classes, working together to accomplish something. One of them is definitely outer. One of them may be inner or outer. The typical reason for making a class an inner class is something along the lines of "It's not a top level abstraction" or "instances of this really do have a very limited context in which they make sense. And that context includes having a pointer to (and being owned by) an instance of the outer class."
Once you have one of these statements, maybe the second class should be an inner one. But what does this have to do with refactoring? I'm guessing some LawOfDemeter style argument could be made at some point.
But never seems way too strong a statement. I've been using public inner classes to define complicated return values (ala UseObjectsToReturnMultipleValues or ResultObject) for some time now and it seems to result in a fairly pleasant code structure.
Here is another discussion about UsageOfInnerClasses
I find inner classes very useful in the form of inner interfaces. For instance, I have a static method findFirst that finds the first element of a Collection that matches a predicate. Obviously, I need some interface for predicates, but I'm not bold enough to write a top-level Predicate interface -- I just haven't thought it through deeply enough to do that. So I just put the interface into the same class as findFirst and can be confident that other people see that this interface is intended for a very limited purpose. -- MichaelSchuerig
Sometimes it makes sense to declare inner classes as public (but definitely static) for packaging convenience. There is an example of it in javax.swing.text.DefaultEditorKit.
Tsolak Petrosian
I think events and listeners would be good candidates for static inner classes. For example, the java.awt.Component class could have had ComponentEvent? and ComponentListener? static inner classes, since their use is exclusively bound to the usage of Component.
Chris Dailey
Yes, but could those be private inner classes? (I'm not familiar with AWT.)