Published 2005-12-30.
Time to read: 1 minutes.
I've been writing Java code for years. Today I learned something new. I have been adding a new feature to Zamples that was best expressed as a doubly nested inner class. The hierarchy looks like this:
public class CodeRange { public CodeRange(String str) { /* ... */ }
public class RangeSpec { public RangeSpec(int i, int j) { /* ... */ } public RangeSpec(RangeItem start, RangeItem end) { /* ... */ }
public class RangeItem { public RangeItem(String str, int i) { /* ... */ } } } }
When it came time to write JUnit tests, I needed to instantiate the hierarchy. The syntax surprised me:
CodeRange.RangeSpec.RangeItem range = new CodeRange("test").new RangeSpec(1, 1).new RangeItem("middle", 3);
Not exactly intuitive, eh? It gets even more interesting when trying to write unit tests for the second RangeSpec constructor, the one that accepts two RangeItems
. I found I had to create a protected no-args constructor for RangeSpec
with an empty body, plus a method within RangeSpec
to create RangeItems
on demand:
/** For JUnit only */ protected RangeSpec() {}
/** For JUnit only */ protected RangeItem newRangeItem(String searchString, int offset) { return new RangeItem(searchString, offset); }
Now I could write my test case setup code:
codeRange = new CodeRange(code, ""); CodeRange.RangeSpec.RangeItem rangeItemStart = codeRange.new RangeSpec().newRangeItem("middle", 3); CodeRange.RangeSpec.RangeItem rangeItemEnd = codeRange.new RangeSpec().newRangeItem("back", 0);
CodeRange.RangeSpec rangeSpec = codeRange.new RangeSpec(rangeItemStart, rangeItemEnd); /* ... */
Everything I read about doubly nested classes amounted to a warning to the effect that they should be avoided. I never had occasion to need this type of solution before, however the particular problem I am solving yields very nicely to this approach. It is simple, elegant and efficient. Don't believe everything you read (except this blog!) 😉.