Imagine you have a static no-argument method which is idempotent and always returns the same value, and may throw a checked exception, like so:
class Foo {
public static Pi bar() throws Baz { getPi(); } // gets Pi, may throw
}
Now this is a good candidate for a lazy singleton, if the stuff that constructs the returned Object is expensive and never changes. One choice would be the Holder pattern:
class Foo {
static class PiHolder {
static final Pi PI_SINGLETON = getPi();
}
public static Pi bar() { return PiHolder.PI_SINGLETON; }
}
Unfortunately, this won't work because we can't throw a checked exception from an (implicit) static initializer block, so we could instead try something like this (assuming we want to preserve the behavior that the caller gets the checked exception when they call bar()
):
class Foo {
static class PiHolder {
static final Pi PI_SINGLETON;
static {
try {
PI_SINGLETON = = getPi(); }
} catch (Baz b) {
throw new ExceptionInInitializerError(b);
}
}
public static Pi bar() throws Bar {
try {
return PiHolder.PI_SINGLETON;
} catch (ExceptionInInitializerError e) {
if (e.getCause() instanceof Bar)
throw (Bar)e.getCause();
throw e;
}
}
At this point, maybe double-checked locking is just cleaner?
class Foo {
static volatile Pi PI_INSTANCE;
public static Pi bar() throws Bar {
Pi p = PI_INSTANCE;
if (p == null) {
synchronized (this) {
if ((p = PI_INSTANCE) == null)
return PI_INSTANCE = getPi();
}
}
return p;
}
}
Is DCL still an anti-pattern? Are there other solutions I'm missing here (minor variants like racy single check are also possible, but don't fundamentally change the solution)? Is there a good reason to choose one over the other?
I didn't try the examples above so it's entirely possible that they don't compile.
Edit: I don't have the luxury of re-implementing or re-architecting the consumers of this singleton (i.e., the callers of Foo.bar()
), nor do I have the opportunity to introduce a DI framework to solve this problem. I'm mostly interested in answers which solve the issue (provision of a singleton with checked exceptions propagated to the caller) within the given constraints.
Update: I decided to go with DCL after all, since it provided the cleanest way to preserve the existing contract, and no one provided a concrete reason why DCL done properly should be avoided. I didn't use the method in the accepted answer since it seemed just to be a overly complex way of achieving the same thing.
The "Holder" trick is essentially double checked locking performed by JVM. Per spec, class initialization is under (double checked) locking. JVM can do DCL safely (and fast), unfortunately, that power isn't available to Java programmers. The closest we can do, is through a intermediary final reference. See wikipedia on DCL.
Your requirement of preserving exception isn't hard: