Instantiating Java Inner Classes

2005-12-30 / All Blog posts

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 = RangeSpec().newRangeItem("middle", 3);
   CodeRange.RangeSpec.RangeItem rangeItemEnd = RangeSpec().newRangeItem("back", 0);

   CodeRange.RangeSpec rangeSpec = 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!) ;)

comments powered by Disqus