Java Hard Puzzle | Puzzle 38: The Unwelcome Guest


Puzzle 38: The Unwelcome Guest
The program in this puzzle models a system that attempts to read a user ID from
its environment, defaulting to a guest user if the attempt fails. The author of the
program was faced with a situation whereby the initializing expression for a static
field could throw an exception. Because Java doesn’t allow static initializers to
throw checked exceptions, the initialization must be wrapped in a try-finally
block. What does the program print?

package javapuzzlers;

public class TheUnwelcomeGuest {
public static final long GUEST_USER_ID = -1;
private static final long USER_ID;
static {
try {
USER_ID = getUserIdFromEnvironment();
} catch (IdUnavailableException e) {
USER_ID = GUEST_USER_ID;
System.out.println("Logging in as guest");
}
}
private static long getUserIdFromEnvironment()
throws IdUnavailableException {
throw new IdUnavailableException(); // Simulate an error
}
public static void main(String[] args) {
System.out.println("User ID: " + USER_ID);
}
}
class IdUnavailableException extends Exception {
IdUnavailableException() { }
}


Output:

$javac HelloWorld.java
HelloWorld.java:9: error: variable USER_ID might already have been assigned
 USER_ID = GUEST_USER_ID;
 ^
1 error

Explanation:
What’s the problem? The USER_ID field is a blank final, which is a final field whose declaration lacks an initializer [JLS 4.12.4]. It is clear that the exception can be thrown in the try block only if the assignment to USER_ID fails, so it is perfectly safe to assign to USER_ID in the catch block. Any execution of the static initializer block will cause exactly one assignment to USER_ID, which is just what is required for blank finals. Why doesn’t the compiler know this? Determining whether a program can perform more than one assignment to a blank final is a hard problem. In fact, it’s impossible. It is equivalent to the classic halting problem, which is known to be unsolvable in general [Turing36]. To make it possible to write a Java compiler, the language specification takes a conservative approach to this issue. A blank final field can be assigned only at points in the program where it is definitely unassigned. The specification goes to great lengths to provide a precise but conservative definition for this term [JLS 16]. Because it is conservative, there are some provably safe programs that the compiler must reject. This puzzle illustrates one such program.

Comments