Saturday, May 1, 2010

Java simple assignment operator semantics

I'll elaborate on this more once I have the time, but I was surprised by the semantics of a simple assignment in Java. I knew it's not "right-then-left" (then assign), but rather "left-then-right" (then assign). It turns out, though, that this isn't as straightforward as I initially thought.

Here are some snippets:
int[] arr = { };
arr[-1] = arr[-2];   
// Throws ArrayIndexOutOfBoundsException: -2

int[] arr = { };
arr[arr[-1]] = arr[arr[-2]];
// Throws ArrayIndexOutOfBoundsException: -1
int[] arr = null;
arr[-1] = (arr = new int[0])[0];
// Throws ArrayIndexOutOfBoundsException: 0

int[] arr = null;
arr[-1] = (arr = new int[1])[0];
// Throws NullPointerException

Update: HAHAHA Nevermind all that confusion stuff. This is as obvious as it can get:
  1. left
    • get array reference (even if it's null)
    • get index (even if it's out of bounds)
  2. right
    • get right hand side value
  3. assign
    • may throw NullPointerException
    • may throw ArrayIndexOutOfBoundsException
What caught me off guard at first was the (wrong!) assumption that since the array reference and index are evaluated before the right hand side, perhaps it will "fail-fast" throw either NullPointerException (if the reference is null) or ArrayIndexOutOfBoundsException (if the index is out of bounds) sooner rather than later.

This is not the case. The following snippet compiles and runs "fine".

((Object[]) null)[-1] = new Object() {
   { while (Boolean.TRUE); }
};
Analogously, this "fail-slow" mechanism is also the case with instance member access. The following compiles and runs "fine".
((Object) null).equals(new Object() {
   { while (Boolean.TRUE); }
});

No comments:

Post a Comment