Is it ever reasonable to nest Java inner classes more than one level deep?
Kushal Paudyal asked how deep you can nest inner classes in Java. The consensus is that while the language itself imposes no limit, the underlying OS and file system may.
Have you ever found a case where two or more levels of nested inner classes are helpful?
Update (11/28): If you consider enum classes, a second level of nesting can make sense. During some recent refactoring, I briefly had an outer class (an HTTP client), an inner class开发者_开发知识库 (an in-memory cache), and inside the inner class an enum class (for the cache eviction strategies). This seemed okay, but to @Thorbjørn's point, I continued by extracting the cache class and its inner enum up out of the HTTP client class.
No. I have not.
The standard example of a class inside a class is the Builder, where you have a subclass to help create a proper instance given a lot of possible configuration methods.
Personally I would consider a more complex case of nested classes an excellent example of code which needs refactoring.
I have not personally run into a case where more than one proved necessary. I could forsee a case where two may prove useful. I have trouble imagining more than two however.
The example I'm imagining is in Java GUI code. It may be useful in certain circumstances to have a class nested inside an already nested ActionListener.
If you're generating code from some data, nested classes can be a good way of avoiding name collisions.
I saw usage of number of levels of nested classes.
There is a legacy machine named Tandem (of HP), that originally runs code of COBOL/C. There are later "patches" that enable that machine of running java too. The variables structure of COBOL are naturally multi-leveled (even 5 levels is a common case), so in order to allow java to call to COBOL servers the java classes were also multi-leveled in order to simplify the conversion of the data between them.
I agree that this is a very uncommon case, but anyway...
I know I'm recycling an old thread, but it's new to me :)
We use multiple levels for our POJOs for deserializing JSON (with Jackson). Here is a tiny example (made up) of JSON we might get back from a RESTful web service:
{ success: true, response: {
sales: { item: "123", sales: 3, returns: 1 },
inventory: { item: "567", qty: 100 }
}
}
We used to have POJOs set up like:
public class Json1 {
private boolean success;
private JsonResponse response;
}
public class Json1Response {
private JsonResponseSales sales;
private JsonResponseInventory inventory;
}
public class Json1ResponseSales {
private String item;
private int sales;
private int returned;
}
public class Json1ResponseInventory {
private String item;
private int qty;
}
We have lots of these, with a POJO for each web service request we might make. This layout gave us a few niggling doubts:
Notice that this one, relatively simple example gave us four class files to keep up with. Now multiply that by 100s and then a 'difficulty factor' of 3 to account for the fact that most JSON is a lot messier than this. Thousands of files.
The field names are re-used all over the place, and the same field name might have different contents based on which web service. (Imagine that quantities might come back as strings from one web service, int from another, then multiply that by 100s.)
Since these things are tied together in a parent/child relationship, we decided to go with this layout instead.
public class Json1 {
private boolean success;
private JsonResponse response;
public class Json1Response {
private JsonResponseSales sales;
private JsonResponseInventory inventory;
public class Json1ResponseSales {
private String item;
private int sales;
private int returned;
}
public class Json1ResponseInventory {
private String item;
private int qty;
}
}
}
In this case, I'm nested two deep, but it might be more. Maybe up to four deep.
Sure - this is valid sometimes. I just did some a few minutes ago.
For example, in some test code I wrote, I wanted to set up some boilerplate that handled running multiple callables. The boilerplate needed to create multiple instances of a callable chunk of code. In this example, I'm creating an instance of a factory to pass into threadedTest, and that factory creates new callables.
@Test public void testXXXXXXXX() throws Throwable {
threadedTest(new CallableFactory() {
@Override public Callable<Request> create() {
return new Callable<Request>() {
// might have state
@Override public Request call() throws Exception {
// do the steps for this test
return ...;
}};
}});
}
Much more concise than creating two new classes where one just creates the other. Of course there's a readability penalty here until you're used to the style, though...
If you combined this with a template method to control transactions or database connect/close, this could end up three deep. (I've had some code like that; if you do it, make sure you write a dummy sample in your comments and explain the structure well)
精彩评论