It was so tempting to start this blog in the classic style: ‘Nested Classes Considered Harmful’, but people turn you off when they see that sort of thing and rightly so. There are few hard and fast rules in software development, and when you take a strident position you end up in a flame war. You can’t have a conversation. So, forget I wrote the “considered harmful" part.
Let’s start this again.
I have strong feelings about nested classes. I tend to avoid them and I don’t think I'm being irrational. Here’s why.
- Nested classes can make code less readable. If they are short, it’s no problem, but it’s not uncommon to see one that spans half a page. When that happens, the class it's embedded in is unavoidably less readable: it’s interrupted by the nested class. Want to scan up and look at the imports or see what it inherits from? Well you have to scroll over the nested class, and it’s worse when there are declarations above the nested class. You lose the big picture.
- Nested classes (in most languages) have special access to their enclosing scope. “So what?”, I hear you say. Well, whenever you have nested scopes you have the possibility of name conflicts. In many languages, a new scope can silently hide variables and methods. I’ve seen people spend 10 to 20 minutes debugging problems related to nested scopes. Don’t get me wrong, I think it’s great to have a global scope and it’s great to have method scopes. Namespaces are great too, but every level increases the chances of misunderstanding.
- Nested classes are often hard to find. “Pfaaaa!” I hear you say, “I use modern tools, I can find anything in a few keystrokes.” Well, yes, but it’s amazing the number of times that you have to ask yourself where something is in a project, times when you are outside of a tool and file names are your only guidance. Even when you are in your editor or IDE and able to just leap to a definition, you confront the semantic problem: when you arrive at a nested class you have to ask yourself “Gee, what does it pull from the outer scope? Why is it here? Is it hiding anything?”
So, at this point, I guess you can see why I’m not pressing this, why I’m not saying “nested classes considered harmful.” It’s a style thing. I just find that I don’t need them. If I need a non-anonymous class, I have no problem promoting it to normal class scope. I know that many people don’t like that. They look at the cases where their little class is used only inside of another one and say “This is perfect, when I make this class an inner class I'm documenting the fact that it's used locally. I avoid polluting the outer name space.” That’s fine, but, really, if you’re that concerned with polluting the outer name space maybe it's too large already. It’s something to think about.
To me, code is much clearer when nested classes are used sparingly.
But, that’s just me.
[I think TypePad swallowed my comment, so here it goes again:]
In Java, nested classes seem a little out-of-place, but in some languages they are a fundamental organizing principle. Beta pioneered the technique, Scala uses it to a great deal -- check out the cake pattern (http://is.gd/HEU) -- and Gilad Bracha's Newspeak appears to be adept as well.
Posted by: Rafael de F. Ferreira | June 27, 2008 at 05:14 PM
I use inner classes quite a bit for Fluent Interface development. It's awfully handy in that regard, but if the inner classes reach any type of size they get pulled out into their own class.
Posted by: Jeremy D. Miller | June 27, 2008 at 09:58 PM
I think the same can be said of nested and lambda functions. When they're small they can greatly improve the readability of the code by increasing locality of reference. But they can quickly grow to a size that makes it hard to distinguish them from their encapsulating function
Posted by: Ferruccio | June 28, 2008 at 07:06 AM
Seems to me that "X Considered Harmful" ought to be considered more like "Considering the harmful effects of X" rather than "X sucks in every way."
But I suppose it isn't considered that way.
Bugs me sometimes that people choose to take affront at a phrase rather than read and /consider/ the material as a whole.
Posted by: Ron Jeffries | June 29, 2008 at 02:24 AM
I recognised my own shunning of nested classes as an arbitrary limit, like when I used to use an 80 char maximum width. So I thought for a moment about why I find nested classes unreadable, and dropped my indentation from being 8 columns wide to being 1 space.
Now I can happily place multiple classes inside an outer class, pretty much pretending that the outer class is a package with its members being top-level.
Posted by: Ricky Clarkson | June 30, 2008 at 03:43 AM
I find nested classes okay if they are very small (It's not uncommon to find one line implementations of Closure or Predicate inside my classes). Unexpected outer scope creep, like you said, can be an issue though.
The usual approach I take is to put the class in a separate package and have the classes that would have been nested marked as default rather than public so that they aren't visible to the outside world.
Posted by: James Carr | June 30, 2008 at 08:06 AM
Nested classes are useful in situations such as:
- it's a private class that is used only by the parent class
- it's a nested static class used to tell you more about the methods (as opposed to more overloads or methods on the parent class).
An example of the 2nd type, I recently wrote some code for a PermissionsEngine. I could have put more overloads/methods that say ForCurrentUser() but instead you can do:
PermissionsEngine.AssertPermissions(...,params string roleIds);
or...
PermissionsEngine.CurrentUser.AssertPermissions(...)
This makes the library easier to understand, and code easier to read when the engine is used.
Posted by: Sachman Bhatti | June 30, 2008 at 11:20 AM
If classes can't nest how will they have children? I remember one time when I was in Prague reading an article on nesting classes, I found it quite stimulating. Let me pass on a few interesting points:
- Classes often nest in the same place every year.
- You can often substitute a class that is the child of another in recipes, this is called "tasty"
- Classes sometimes share the same mate for years (see bullet 1, they also will use the same nest)
But that said, let me address your points individually:
1. No.
2. Maybe, but have you tried looking in trees?
3. Isn't this the same as 1?
So in conclusion, please let classes nest, or else our children's children may well never see classes.
Posted by: George Harrison | July 09, 2008 at 01:35 PM
George: Thank you for your thoughtful response, however I notice that classes don't really need nests to reproduce. When the male gamete is not involved, they reproduce by burgeoning, or budding.
Posted by: Michael Feathers | July 09, 2008 at 01:49 PM
I think in the general case, you are correct--nested classes are rarely appropriate. Nested classes are like friend classes.
There are times when nested classes make sense. When private state information needs to be given out as opaque data only to be given back to the same class, nested classes to abstract that information make sense. Not implementing something like that as nested classes means implementation details are leaking out of your class. While I don't consider "polluting" a viable reason for nested classes; if those implementation details are made public through a autonomous class, you have to accept that you no longer have the same freedom to change those implementation details.
Posted by: Peter Ritchie | July 10, 2008 at 09:21 AM
One place where I've found using nested classes useful (though hardly essential) is for factory classes. When the factory class is nested inside the class it instantiates, it can have access to methods that are not intended for other clients. Also, it makes sense (sometimes) to keep these together when the two classes are tightly coupled, anyway.
That said, it's been several years since I've used this technique.
Posted by: George Dinwiddie | July 12, 2008 at 11:47 AM
Partial classes in .NET make 1 irrelevant. 3 is true of any member, not just nested classes. 2 is legitimate, but it is the ability to access private scope that make nested classes uniquely suited to solve certain classes of problems (like iterators). They certainly can be abused, like every other programming feature ever invented, but I have never felt a need to avoid them.
Posted by: David Nelson | July 15, 2008 at 04:05 PM
Right now dealing with another often seen side effect of nested classes: duplication hidden underneath an extra layer of (un)visibility.
Posted by: alex | August 18, 2008 at 01:28 PM