tags:

views:

851

answers:

5

The code below doesn't do what I expect. Every string is null after this code executes.

String[] currentState = new String[answer.length()];
for(String x : currentState)
{
    x = "_";
}

The code below does what I expect. Every string in currentState is now "_"

String[] currentState = new String[answer.length()];
for (int i = 0; i < currentState.length; i++) {
    currentState[i] = "_";
}

Can someone explain why the first case doesn't work?

Thanks, jbu

+22  A: 

By design the for each variable 'x' (in this case) is not meant to be assigned to. I'm surprised that it even compiles fine.

String[] currentState = new String[answer.length()]; 
for (String x : currentState) { 
    x = "_"; // x is not a reference to some element of currentState 
}

The following code maybe shows what you're in effect are doing. Note that this is not how enumerations work but it exemplifies why you can't assign 'x'. It's a copy of the element at location 'i'. (Edit: note that the element is a reference type, as such it's a copy of that reference, assignment to that copy does not update the same memory location i.e. the element at location 'i')

String[] currentState = new String[answer.length()]; 
for (int i = 0; i < answer.length(); i++) { 
    String x = currentState[i];
    x = "_";
}
John Leidegren
oh, so when I do (String x : currentState), it creates a NEW string, x, and copies the VALUE of the string in currentState?
nevermind, I see, I'm just switching the reference
"I'm surprised that it even compiles fine". Of course it compiles fine. If you don't want assignment just make x final.
cadrian
It doesn't copy anything. It's just another reference to the same object. Assigning to it changes the reference, not the object (as if the object could be changed-- String is immutable).
Devin Jeanpierre
I was talking to someone recently that had run into this and was looking into adding a FindBugs detector for it. While you *can* do it, the chances that it is a bug when you do it are about 100%. Personally, I'd vote that this should be a compiler error.
Alex Miller
If you want it to be a compiler error mark the variables as final.
TofuBeer
I don't understand the rational behind that, for what reason is it not final by default? And for what reason would you ever really need it to be not final in the first place?
John Leidegren
The same rationale where nothing is final by default, save interface data members, I guess (personally I think everything should be final as default).
TofuBeer
+3  A: 

Because x is a reference (or a variable of reference-type). All the first piece of code does is re-point the reference at a new value. For example

String y = "Jim";
String x = y;
y = "Bob";
System.out.println(x); //prints Jim
System.out.println(y); //prints Bob

The fact that you are re-assigning the reference y to "Bob" does not affect what the reference x was assigned to.

oxbow_lakes
In your sample code x *isn't* a reference. It's a variable whose value is just an int. Compare this with the original code, where the value of x *is* a reference because String is a reference type.
Jon Skeet
Yes, of course. I was just trying to keep it simple. I thought that going in to the semantics of "variable of reference type" and "variable of primitive type" was probably unnecessary. To all intents and purposes, I think that x can be thought of as a reference to some value. Changed type to String
oxbow_lakes
Be better if you did 'y = "Bob";' ... makes it more obvious that when x "points to y" it isn't a pointer to a pointer (something some people get stuck on when the are starting with Java)
TofuBeer
+7  A: 

Original code:

String currentState = new String[answer.length()];

for(String x : currentState) 
{ 
    x = "_"; 
}

Rewritten code:

String currentState = new String[answer.length()];

for(int i = 0; i < currentState.length; i++) 
{ 
    String x;

    x = currentState[i];
    x = "_"; 
}

How I would write the code:

String currentState = new String[answer.length()];

for(final String x : currentState) 
{ 
    x = "_";   // compiler error
}

Rewritten code with the error:

String currentState = new String[answer.length()];

for(int i = 0; i < currentState.length; i++) 
{ 
    final String x;

    x = currentState[i];
    x = "_";   // compiler error
}

Making the variables final highlights when you do things like this (it is a common beginner mistake). Try to make all of your variables final (instance, class, arguments, exceptions in catch. etc...) - only make them non-final if you really have to change them. You should find that 90%-95% of your variables are final (beginners will wind up with 20%-50% when they start doing this).

TofuBeer
A: 

You can convert your array to a List and then iterate like this:

String[] currentState = new String[answer.length()];
List<String> list = Arrays.asList(currentState);
for(String string : list) {
   x = "_";  
}
Luke
A: 

Object x[]={1,"ram",30000f,35,"account"}; for(Object i:x) System.out.println(i); for each is used for sequential access