Puzzle: The throws clause on a constructor declaration


Although it is common to see a throws clause on a method declaration, it is less common to see one on a constructor declaration. The following program has such a declaration. What does it print?
public class Test {

private Test instance = new Test();

public Test() throws Exception {
throw new Exception("Runtime Exception");
}

public static void main(String[] args) {
try {
Test b = new Test();
System.out.println("Surprise!");
} catch (Exception ex) {
System.out.println("Exception throw by constructor");
}
}
}
The main method invokes the constructor of Test class, which throws an exception. You might expect the catch clause to catch this exception and print "Exception throw by constructor". But if you tried running it, you found that it does not print anything : It throws a StackOverflowError. Why? if you have a closer look at the program then you found that the Test instance contains a second internal instance so here according to java specification [JLS 12.5] the variable initializers will run before the body of the constructor.

In this case, the initializer for the variable instance invokes the constructor recursively. That constructor, in turn, initializes its own instance field by invoking the Test constructor again and so on.

These recursive invocations cause a StackOverflowError before the constructor body ever gets a chance to execute. Because StackOverflowError is a subtype of Error rather than Exception, the catch clause in main doesn’t catch it.

It is not uncommon for an object to contain instances of its own type. You must initialize such contained instances carefully to avoid a StackOverflowError.

Purport of this puzzle
Constructors declared to throw exceptions, note that a constructor must declare any checked exceptions thrown by its instance initializers.

This program, which illustrates a common service-provider pattern, won’t compile, because it violates this rule
public class Test {

private ServiceProvider serviceProvider = new ServiceProvider();
private Consumer consumer = (Consumer) serviceProvider.newInstance();

public Test() { // we must need to declare IllegalAccessException
            // empty body
}

class ServiceProvider {
public ServiceProvider newInstance() throws IllegalAccessException {
return new ServiceProvider();
}
}

class Consumer extends ServiceProvider {

}
}
Although it has no body, the constructor throws checked exceptions as IllegalAccessException. Which is thrown by serviceProvider.newInstance(), which is called when initializing the consumer field. The best way to fix this is to create a private static helper method that computes the initial value of the field and handles exceptions appropriately.

The following version of Test compiles without error
public class Test {

private static ServiceProvider serviceProvider = new ServiceProvider();
private Consumer consumer = newConsumer();
private static Consumer newConsumer() {
try {
return (Consumer) serviceProvider.newInstance();
} catch (IllegalAccessException e) {
throw new AssertionError(e);
}
}

// Fixed - instance initializers don’t throw checked exceptions
public Test() {

}

static class ServiceProvider {
public ServiceProvider newInstance() throws IllegalAccessException {
return new ServiceProvider();
}
}

class Consumer extends ServiceProvider {

}
}
In summary, instance initializers run before constructor bodies. Any exceptions thrown by instance initializers propagate to constructors. If initializers throw checked exceptions, constructors must be declared to throw them too, but this should be avoided, because it is confusing.

Comments

Popular Posts

Java Conversion Types and Conversion Contexts

Load Balancing usign HAProxy for Openfire

Enable JMX Port in Tomcat with authentication