Wednesday, August 11, 2010

Jackson and Inner Classes: yes, you can use, but they must be STATIC inner classes

Ok here is something I did not realize up until today: apparently proper usage of inner classes is an area of vast confusion within Java developer community. In fact there are a few web pages that suggest that Jackson could not be used with inner class values.

This is actually both true and false, due to pecularities of Java inner classes. Although inner classes were introduced as early as with Java 1.1 (to make working with AWT and then Swing easier, mostly), not everyone knows enough to use proper kind of inner class.

1. Long story short?

You can use static inner classes like this one:

  public class Outer {
    static class Inner {
      public int getX() { return 5;
    }
  }    

for values without any problems. Just make sure that "static" is in there and you are golden.

So what is the problem? If you do not add static, resulting class is generally useless for Jackson as well as any other data binding framework (Hibernate, JAXB); can often be serialized, but never serialized into. To understand why, let's go back in time, to late 90s... to time when the first big batch of syntactic sugar was added in Java.

2. Anonymous, static, non-static inner classes?

Basically there are multiple kinds of inner classes: anonymous inner classes are ones used inline (for event handling for example); static ones are ones explicitly declared and have modifier 'static', and non-static ones are like static ones except for the keyword.

The important differentiator here is the keyword "static", or lack thereof. Modifier chosen is not very intuitive, but what it really means is this: non-static inner classes (including anonymous ones) have set of hidden variables added by compiler, passed via (hidden) constructor. And as a consequence, do not have zero-argument ("default") constructor -- even if it might appear one was being used by code.

So basically if you write something like:

  public class Outer {
   class Inner { // non-static
     public int getX() { return 3; }
   }
  }

what compiler actually generates is more like:

public class Outer { ... }

class Outer$Inner {
  private final Outer parent;

  Outer$Inner(Outer p) {
    parent = p;
  }
  public int getX() { return 3; }
}

(and similarly for anything within "Outer" that code in "Inner" would access)

Why? Because this way code in Inner can actually access all members (including private ones!) within enclosing class; and in case of anonymous (inline) inner classes, even seemingly variables within scope (this is just smoke and mirrors -- final variables are passes as just more hidden constructor arguments).

And static inner classes are then just plain boring classes with no hidden baggage. In fact, they don't really differ from "secondary" classes (non-public classes declared in same source file as the main public class) by anything other than name which uses enclosing class for namespacing.

3. So about Jackson, inner classes... ?

The basic reason for Jackson's refusal to try use non-static inner classes for deserialization (serialization actually works fine) is because there is no general way to instantiate such classes -- there is no zero-argument constructor, nor @JsonCreator annotated other constructors or factory methods (or single-String-argument constructors, but I digress). Jackson can not instantiate them.

In theory one could handle this case if it was known what the enclosing parent class instance was. But realistically this is both complicated and unnecessary -- typically omission of "static" is either accidental, or incidental, and can be added to make things work.

blog comments powered by Disqus

Sponsored By


Related Blogs

(by Author (topics))

Powered By

About me

  • I am known as Cowtowncoder
  • Contact me at@yahoo.com
Check my profile to learn more.