Is ‘final’ keyword always safe to add/remove?

If somebody asked me this question “Is it always safe to put/remote ‘final’ keyword on a read only local variable?”, honestly I would have answered “Yes”.

Until I read JavaPuzzlers by Joshua Bloch and Neal Gafter and realized what a little variant of puzzle #8 would look like:

What’s the output of this code?

public static void main(String[] args)
{
    int z1 = 0;
    final int z2 = 0;

    System.out.println(false ? z1 : 'X');
    System.out.println(false ? z2 : 'X');
}

9 thoughts on “Is ‘final’ keyword always safe to add/remove?”

  1. The behavior comes from the keywork ‘final’ or from ternary operator [(condition) ? instruction if true : instruction if false]? Indeed, the result of System.out.println(false ? z1 : ‘X’); is weird. The result of System.out.println(false ? z1 : z2); is “normal”.

  2. Surprising, indeed !

    I did some investigation using ‘javap -c’ and it appears the ‘final int z2’ declaration got pruned by the compiler which then decides the second println call takes a char and not an int: the type of the ‘false ? z2 : ‘X” is ‘char’ and not ‘int’ as it is in the first case. This is only possible because the compiler infers the conditional trigram always triggers its false branch.

    Here is the code with final:

    0: iconst_0
    1: istore_1
    2: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
    5: bipush 88
    7: invokevirtual #3; //Method java/io/PrintStream.println:(I)V
    10: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
    13: bipush 88
    15: invokevirtual #4; //Method java/io/PrintStream.println:(C)V
    18: return

    and the code without final which producers two identical instructions:


    0: iconst_0
    1: istore_1
    2: iconst_0
    3: istore_2
    4: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
    7: bipush 88
    9: invokevirtual #3; //Method java/io/PrintStream.println:(I)V
    12: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
    15: bipush 88
    17: invokevirtual #3; //Method java/io/PrintStream.println:(I)V
    20: return

  3. Nice one! I think this is simply due to the fact that the compiler replaces the “final int” with a “final char” since the value is short enought.

    So to me, it has nothing to see with using the ternary operator, except that it’s a good way to show which type the variable actually has.

    Here are some more tests to demonstrate what happens :

    public static void main(String[] args) {
    int z1 = 0;
    final int z2 = Character.MAX_VALUE;
    final int z3 = Character.MAX_VALUE + 1;
    final int z4 = z1 + z1;
    final int z5 = 0 + 0;

    System.out.println(false ? z1 : ‘X’); // 88 : z1 is an int
    System.out.println(false ? z2 : ‘X’); // X : z2 is a char
    System.out.println(false ? z3 : ‘X’); // 88 : z3 is an int
    System.out.println(false ? z4 : ‘X’); // 88 : z4 is an int
    System.out.println(false ? z5 : ‘X’); // X : z5 is a char
    }

  4. I looked into the java language spectification and found this about the conditional operator ‘?’ (§15.25)

    «If one of the operands is of type T where T is byte, short, or char, and the other operand is a constant expression of type int whose value is representable in type T, then the type of the conditional expression is T.»

  5. Hehe, nice one!

    My guess (and I haven’t read Java puzzlers) is that this relates to the evaluation rules of the ternary operator, as described in §15.25 of the Java spec:
    “- If one of the operands is of type T where T is byte, short, or char, and the other operand is a constant expression of type int whose value is representable in type T, then the type of the conditional expression is T.
    […]
    – Otherwise, binary numeric promotion (§5.6.2) is applied to the operand types, and the type of the conditional expression is the promoted type of the second and third operands.”

    Constant expressions are defined by §15.28.
    z2 (being final) is a constant expression, so the first case above applies.
    z1 is not, so we are in the second case.

    http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.25
    http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.28

  6. Thank you those who tried to solve and understand this puzzler. We are indeed facing the classical ‘binary numeric promotion in a mixed type ternary expression applied to an integer constant representable in type char’ problem…

Comments are closed.