1

Is the scope of the enhanced for loop (EFL) variable different for different classes in Java?

When I use an EFL over an ArrayList containing Integers I cannot modify their values directly, however if I do the same thing for an ArrayList of SimpleObjects, defined in my code below, I can change the values of the instance variables with no problem.

import java.util.ArrayList;

class SimpleObject{
  public int x;
  public SimpleObject() {
    this.x = 0;
  }
}

public class Simple {
  public static void main(String args[]){
    // Create an arraylist of Integers and SimpleObjects
    ArrayList<Integer> intList     = new ArrayList<Integer>();
    ArrayList<SimpleObject> soList = new ArrayList<SimpleObject>();

    // Add some items to the arraylists
    intList.add(1);
    intList.add(2);
    soList.add(new SimpleObject());
    soList.add(new SimpleObject());

    // Loop over the arraylists and change some values
    for (Integer _int : intList) {
      _int = 3; // Why doesn't this work but so.x = 5 below does?
    }
    for(SimpleObject so : soList) {
      so.x = 5;
    }

    // Loop over the arraylists to print out values
    for (Integer _int : intList) {
      System.out.println("integer = " + _int);
    }
    for(SimpleObject so : soList) {
      System.out.println("      x = " + so.x);
    }
  }
}

Actual output:

integer = 1
integer = 2
      x = 5
      x = 5

Expected output:

integer = 3
integer = 3
      x = 5
      x = 5

so my question is why doesn't _int = 3; persist in the first EFL, but so.x = 5; does in the second EFL?

compuphys
  • 1,289
  • 12
  • 28
  • 1
    `_int` is a *copy* of the reference to the `Integer`, so you are reassigning the `_int` variable, which is *local* to the `for` loop iteration. As soon as the loop is exited, the `_int` variable is out of scope and the `Integer` object would be garbage collected (if it was not cached by the `Integer` class, but that's another story). – MC Emperor May 06 '20 at 15:50

3 Answers3

1

The difference is not that somehow scope rules are different, but that you do two different actions:

  • so.x = 3 sets the field x of the object referenced by so to 3.
  • _int = 3 sets _int to 3.

If you changed so.x = 3 to so = new SimpleObject you would see that this too doesn't influence the content of the list you iterate over.

Joachim Sauer
  • 302,674
  • 57
  • 556
  • 614
1

Is the scope of the enhanced for loop (EFL) variable different for different classes in Java?

No. The scope of a variable declared in a for loop or extended for loop is the body of the loop.

Why doesn't _int = 3; persist in the first EFL, but so.x = 5; does in the second EFL?

In the second example, you are not assigning to the loop variable so.

You are assigning to so.x. That is a field of the object that the so variable refers to.

Then later you iterating over the list again and accessing the object which you previously updated.

The so variables in the two loops are different variables.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
1
// Loop over the arraylists and change some values
for (Integer _int : intList) {
  _int = 3; // Why doesn't this work but so.x = 5 below does?
}
for(SimpleObject so : soList) {
  so.x = 5;
}

It is because you are assigning the value to _int but you are changing the value in so. Assignment and changing the value are completely different things.

You can understand it better with the following example:

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        final List<String> list = new ArrayList<String>();

        // Changing - will work
        list.add("Hello");
        list.add("World");

        // Assigning - won't work
        list = null;
    }
}

If you want to change intList, you can use List:set as shown below:

import java.util.ArrayList;

public class Main {
    public static void main(String[] args) {
        ArrayList<Integer> intList = new ArrayList<Integer>();
        intList.add(1);
        intList.add(2);

        for (int i = 0; i < intList.size(); i++) {
            intList.set(i, 3);
        }

        // Display
        intList.stream().forEach(System.out::println);
    }
}

Output:

3
3
Arvind Kumar Avinash
  • 71,965
  • 6
  • 74
  • 110