It's not a secret anymore!
The moose likes Java in General and the fly likes reflections on java Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login
JavaRanch » Java Forums » Java » Java in General
Bookmark "reflections on java" Watch "reflections on java" New topic

reflections on java

Douglas Chorpita
Ranch Hand

Joined: May 09, 2006
Posts: 97
This question is derived from another topic called "strange notation".

I started a new topic because I am interested in a subtle aspect of the original question. The code has been changed slightly to emphasize my point.

The output is 6.

This leads to an important observation which is (perhaps) not so obvious.

Array index expressions - and more generally method parameter expressions - are computed before testing to see whether the object reference is valid.

From a performance perspective, it might be more sensible if the JVM would test for a valid reference (which is probably one machine instruction) before it computes all the parameters (which could be tens of thousands of machine instructions if the parameter expressions involve method calls, etc.).

Is this behavior defined by the Java language? Or is it JVM specific?

SCJP 1.4 - 95%
marc weber

Joined: Aug 31, 2004
Posts: 11343

Interesting question. But I think it's beyond the scope of the SCJP exam, so I'm moving this to Java in General (intermediate).

"We're kind of on the level of crossword puzzle writers... And no one ever goes to them and gives them an award." ~Joe Strummer
Jim Yingst

Joined: Jan 30, 2000
Posts: 18671
It's specified in JLS 15.13.1. Is this unnecessarily slow from a performance perspective? In my opinion, no. It's only an issue if the programmer has screwed up and provided a null reference; if that happens, the performance issue is extremely minor compared to the fact that you're throwing a NullPointerException. If the programmer has not screwed up, then I think it makes no difference performancewise when the check is performed. It's probably simplest to perform the check immediately before the array is actually used, since that's also the point at which an ArrayIndexOutOfBoundsException would be thrown if the array index is invalid. You can't possibly perform the latter check without knowing both the array reference and the index. It makes a certain sense to me to put those two checks together at nearly the same time, even though the null check could have been performed earlier.

"I'm not back." - Bill Harding, Twister
Ernest Friedman-Hill
author and iconoclast

Joined: Jul 08, 2003
Posts: 24199

OK, so I wanted to write a little bit of code that would fail if the check was done first, and work if it was done second. When I compile it with either JDK 1.4.2 or 1.5, it gets an NPE, which surprised me. Here's the code:

int[] array1 = null;
int[] array2 = {1, 2, 3};
array1[(array1 = array2)[1]] = 4;

Here's the disassembly of that crazy line:

18:aload_1 // Push array1 (null) on the stack
19:aload_2 // Push array2 on the stack
20:dup // Push array2 again
21:astore_1 // Store array2 into array1, leaving array2 on the stack
23:iaload // Read an item from array2, leaving array1/null on the stack
25:iastore // Try to store 4 into null, and NPE

Note what's happening: we effectively check for the null reference after the index is computed, but the reference we check is the one from before the index was computed. Jim, want to explain if this one's intentional?

[Jess in Action][AskingGoodQuestions]
Jim Yingst

Joined: Jan 30, 2000
Posts: 18671
I think it's intentional. It's an extension of the "evaluate left before right" theme that runs thoroughout the rules of evaluation. In an array access expression list foo[bar], the array reference foo is left of the index bar, so it's evaluated first. When later you evaluate the right expression bar, there may be side effects changing foo, but we ignore them here because foo has already been evaluated. If we were able to check the value of foo after the expression was evaluated, we'd find that yes, it's been changed as a side effect of evaluating bar. But that new value does not affect the evaluation of the array access expression - it's only visible after that evaluation.

In a sense, it's not unlike this situation:

The final values are x = 1, y = 1. The y = 1 comes about because when (x + (++x)) is evaluated, the x on the left evaluates to 0 and the one on the right evaluates to 1. The fact that x is now 1 does not change the value that the first x had at the time it was evaluated.
[ July 14, 2006: Message edited by: Jim Yingst ]
Douglas Chorpita
Ranch Hand

Joined: May 09, 2006
Posts: 97
Indexing arrays and passing parameters to methods both behave the same. If you think about method calls, maybe there is some logic in this behavior.

This is from Chapter 15 on Expressions:

15.12.4 Runtime Evaluation of Method Invocation

At run time, method invocation requires five steps. First, a target reference may be computed. Second, the argument expressions are evaluated. Third, the accessibility of the method to be invoked is checked. Fourth, the actual code for the method to be executed is located. Fifth, a new activation frame is created, synchronization is performed if necessary, and control is transferred to the method code.

Testing for a null pointer is an aspect of Step 3.

Evaluating the parameter expressions is part 2.

It makes sense that the behavior should be consistent. Step 3 "determining whether the method to be invoked is accessible", in our case, means checking to see if the pointer is null. But it could also mean, in a more complicated case, that a whole bunch of parameter marshaling needs to be done when we are doing RMI. Right? Maybe this is the reason for the ordering.

I am just guessing.

But for a remote method invocation, we need to evaluate parameters before we can see whether our (remote) object reference will find the method it is looking for.

What do you think?
[ July 14, 2006: Message edited by: Douglas Chorpita ]
I agree. Here's the link:
subject: reflections on java
It's not a secret anymore!