Wednesday, October 24, 2007

Stupid Java superclass constructors


Apparently, constructors in Java suck. Consider a following example:

public class Page {
protected int page;
protected int pageSize;
protected List result;

public Page(int page, int pageSize) {
this.page = page;
this.pageSize = pageSize;
findResult();
}

protected void findResult() {
// do something with page & pageSize
result = doQuery(…, page, pageSize);
}

public List getResult() {
return result;
}
}

Now you want to introduce a subclass which contains different implementation of findResult(). But, let’s also say you decide you want to use an additional parameter.

public class OrderedPage {

protected int sortColumn;

public OrderedPage(int page, int pageSize, int sortColumn) {
super(page, pageSize);
this.sortColumn = sortColumn;
}

@Override
protected void findResult() {
// do something with page & pageSize & sortColumn

}

}

Well, the problem is that sortColumn field in the above class will be uninitialized when findResult() methods is invoked.

If you’re pressed for time and able to change the base class, you can add a protected, no-arg constructor, and repeat the whole initialization and the method call of findResult() in the OrderedPage class again. The cleanest approach would involve rewriting all the public getters, like getResult() according to lazy-loading approach, and remove the invocation from the base class constructor. Like this:


public List getResult() {
if (result == null) {
result = findResult();
}
return result;
}

However, you might have a situation where there are multiple getters to change. Altering them, apart from being a tedious job, might involve slight semantic changes to a class eg. a variable declared final will have to be “unfinalized”.

And, let’s not forget, you might not actually be able to change the base class.

There is, of course, a hack. It consists of using a static method and ThreadLocal variable:

public class OrderedPage {

private static final ThreadLocal<Integer> threadSortColumn =
new ThreadLocal<Integer>();


private static int hack(int pageSize, int sortColumn) {
threadSortColumn.set(Integer.valueOf(sortColumn));
return pageSize;
}

protected int sortColumn;

public OrderedPage(int page, int pageSize, int sortColumn) {
super(page, hack(pageSize, sortColumn));
}

@Override
protected void findResult() {
// do something with page & pageSize & sortColumn
sortColumn = threadSortColumn.get().intValue();

}

}

Quite surprisingly, it doesn’t look that bad! And it will confuse the hell out of the junior developers, as well as senior wannabes.

Code tight!