The Friday Free Stuff extravaganza is about to begin.
first installment: the solution to last week's puzzler which comes to us straight from the personal (!) email accounts of techno celebs extraordinarie(s) Dr. Josh Bloch and Dr. Neal Gafter:
In last week's puzzler, you were given a library class:
public class Foo {
public Foo(int i) {
...;
}
...
}
Because it's a library class, you have no access to its internals and you can't modify it. Suppose you want to write a subclass:
public class Bar extends Foo {
private final int j;
public Bar() {
super(j = SomeOtherClass.f());
...
}
}
Unfortunately this isn't legal. If you try it, you'll get this error message:
Bar.java:13: cannot reference j before supertype constructor has been called
super(j = SomeOtherClass.f());
^
You were asked to refactor the program to achieve the desired effect, subject to the following constraints:
- Class
Foo does not have an accessor to return i.
- You can't call
SomeOtherClass.f() twice, as there is no guarantee that successive calls will return the same value.
- Your program must be thread-safe: multiple threads may invoke the constructor
Bar() concurrently.
- Elegance counts.
- Don't code like my brother.
You did very well indeed! The winner was Chris Nokleberg who posted a correct, complete, elegant solution 26 minutes after Mary posted the problem. Twenty-six minutes! Can you believe it?
The basic idea of Chris's solution was to interpose a private constructor in between the public parameterless constructor and the superclass constructor
public class Bar extends Foo {
private final int j;
public Bar() {
this(SomeOtherClass.f());
}
private Bar(int f) {
super(f);
j = f;
}
}
This is exactly the solution that we were looking for. It's easy if you see it, impossible if you don't.
Second place goes to Michael Nascimento Santos, who came up with the following solution, in which the result of invoking SomeOtherClass.f() is stashed in a static thread-local variable prior to invoking the superclass constructor. The value can then be copied into the instance variable after the superclass constructor is invoked:
public class Bar extends Foo {
private static class ThreadLocalCachedValue {
private final ThreadLocal threadLocal = new ThreadLocal();
public int setValue(int value) {
threadLocal.set(new Integer(value));
return value;
}
public int getValue() {
return ((Integer)threadLocal.get()).intValue();
}
};
private static ThreadLocalCachedValue cachedValue =
new ThreadLocalCachedValue();
private final int j;
public Bar() {
super(cachedValue.setValue(SomeOtherClass.f()));
j = cachedValue.getValue();
}
}
Michael submitted his solution a mere eight minutes after Chris. You guys are unbelievably fast! Michael's solution was, however, less elegant than Chris's. Kris Schneider submitted a similar solution to Michael's that avoided the use of a nested class by wrapping the ThreadLocal.set call in a static method on Bar. The ThreadLocal solution can be made a bit more elegant by using the new ThreadLocal.remove method added in JDK 5:
public class Bar extends Foo {
private final int j;
private static ThreadLocal<Integer> sleaze = new ThreadLocal<Integer>() {
protected Integer initialValue() {
return SomeOtherClass.f();
}
};
public Bar() {
super(sleaze.get());
j = sleaze.get();
sleaze.remove();
}
}
This solution is still a good deal less elegant than Chris's. There's a reason we called the ThreadLocal variable sleaze.
A crate of rotten halibut goes to Ron Yang and Ka-Hing Cheung, who didn't check to see whether their programs compiled before posting them. Hint: they don't.
Tom Hawtin proposed a solution wherein he interposed a class between Foo and Bar, and stashed the results of SomeOtherClass.f() in an instance variable of the enclosing class. This works, but arguably it violates the conditions of the problem. Similarly, Tom proposed the use of a static factory, but the problem states (albeit subtly) that Bar must have a public parameterless constructor. Normally we're big fans of static factories, but occasionally you don't have that option. Sometimes you need your class to be extendable. The same restriction rules out Tom's inner-class based solution. However, Tom gets an honorable mention for submitting three (!) solutions.
Bob Lee corectly points out that if you're writing JVM byte code rather than Java source code, you can simply set the instance variable before invoking the superclass constructor. This is, in fact, how the compiler initializes the enclosing-instance field for instances on non-static nested classes.
Nikita Tovstoles submitted a convoluted by correct solution in which a single static variable was used to stash the results of SomeOtherClass.f() as the superclass constructor was executed. Hairy synchronization code was required to make it work in the presence of concurrency. Click and Hack characterize this solution as ingenious-but-yucky.
So the big prize package...

... goes to Chris Nokleberg!
But wait, there's more.... Michael, Kris, Tom, Bob and Nikita all get t-shirts because Click and Hack said nice stuff (to varrying degrees) about each of you!!!
If you end up looking as cute in your t-shirt as Vanessa does...

... send me a picture and maybe I'll post it.
Ron and Ka-Hing: if I had any rotten halibut I'd send it to you. Lucky for you I'm fresh out.
So that closes out last week's puzzler. But we're not done yet. More Friday Free Stuff is on your way as this MaryMaryQuiteContary special Friday extravaganza continues... stay tuned.
:-)
mary
p.s. bookmark. shift. reload.
p.p.s. i kindly ask that you lucky winners send me email with your mailing address using the following formula: first.last@sun.com. my firstname is mary. my lastname is smaragdis. i will use your personal info for the singular purpose of copying it down onto an envelope (or in Chris' case, a box) so that I can send you the free stuff. I won't share your personal info with anybody. I won't use it for any other purpose whatsoever. After I copy it down I'll delete the mail.
p.p.p.s. Don't get any ideas about impersonating a winner, people. It won't work. And if you try I will black list you.