Skip to main content

JAXB 2.0 and Immutable Objects/Fields

Posted by kohsuke on February 22, 2005 at 5:59 PM PST

In the last week, I wrote about how you can use JAXB 2.0 as an XML persistence engine, and I got a lot of comments about the support for immutable objects. So I'd like to elaborate on it today.


First, disclaimer. As usual, everything I write here can be changed by the time the spec becomes final. That's the whole point of writing this --- so that I can get more feedbacks and send them to the expert group.


It is often common to make some fields settable only by a constructor. This is done by the final keyword, and it usually improves the code readability. If you do this for all the fields of an object, it's called an immutable object, and it is also considered as a useful design pattern.


The following is a simple example of an immutable object:

public class Point {
    public final int x,y;
    public Point(int x,int y) {
       this.x = x;
       this.y = y;
    }
}

Unfortunately, the above class is not supported by the current release of the JAXB RI 2.0, and here's the various issues surrounding this.


Portability

First, creating an instance of an object without calling a constructor is not a committed part of the JDK core class library. It is doable through sun.misc.Unsafe, and that's what the Java serialization uses, but unless you are a part of the J2SE, you cannot reliably use this method. Even though, this undocumented API is actually available on many JREs, including IBM's and BEA's, it's still awkward for a spec to require a functionality that's not publicly committed. This is one reason why the current release of the JAXB RI doesn't support it.


Final field

Some XML data-binding works by injecting additional byte code into the class files. For them, setting a value to a final field is more difficult, if not impossible. Since a spec usually prefers to enable many different ways to implement, this is an issue.



I should mention that this is NOT a problem for the RI, because we can fall back to Java reflection. An user voidmain reported that this doesn't work in the current RI, and he's right that it doesn't. But this is just a bug in the RI, not a fundamental issue. This problem has been already fixed internally.


Constructor as a setter

Another user mgrev mentioned that perhaps we could invent an annotation to put on a parameter of the constructor, perhaps like this:

public class Point {
    public final int x,y;
    public Point(@XmlConstructor("x") int x, @XmlConstructor("y") int y) {
       this.x = x;
       this.y = y;
    }
}

While this works, it is somewhat awkward. It gets even more awkward with a case where a constructor does a non-trivial computation (for example, taking an ID and a JDBC RowSet and filling in the rest of the fields from this row set), although this is very common. You have to define a separate constructor just for making JAXB work.


It is also worth pointing out that the Java class files do not retain the names of the method parameters (only as debug information, and reliably accessible at the runtime), so you have to give those names manually to the annotations.




Those are the issues that surround this problem. Personally, I prefer to use sun.misc.Unsafe. That's the simplest for users, and it's available in the most of the JREs available today (I mean, what other JREs are there beside those three?), and even if this particular interface is not available in your favorite JRE, there still must be an equivalent functionality in it, or else Java serialization won't work. But this is a slippery slope.


Meanwhile, to work around this in the RI, you can define a private no-arg constructor, and eliminate all final fields, like this:

public class Point {
    public /*final*/ int x,y; // can't mark them final because of JAXB
    public Point(int x,int y) {
       this.x = x;
       this.y = y;
    }
    private Point() {} // for JAXB
    public int getX() { return x; }
    public int getY() { return y; }
}

If you have any thoughts, please let us hear it at the forum.