0

I've been learning java and wanted to know if it's possible to construct objects that are inside an array using foreach loop. Using for-loop it's very easy:

public class Bottle {

    double waterAmount;

    Bottle(){
        waterAmount = 1.0;
    }

    public static void main(String[] args) {

        Bottle[] bottles = new Bottle[3];
        //foreach
        for (Bottle bottle : bottles) {
            bottle = new Bottle();
            System.out.println(bottle.waterAmount);
        }
        //for
        for (int i = 0; i<bottles.length;i++){
            bottles[i] = new Bottle();
            System.out.println(bottles[i].waterAmount);
        }

        System.out.println("index 1: " + bottles[1].waterAmount);
    }
}

When I'm running this program using for loop I get:

1.0
1.0
1.0
index 1: 1.0

which is ok because the array of bottles has been constructed properly. When I execute it using only foreach there is the output:

1.0
1.0
1.0
Exception in thread "main" java.lang.NullPointerException
    at Bottle.main(Bottle.java:31)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)

So as much as I understand the bottle inside foreach constructs every bottle object but then it's not asigning those new bottles to the every index of an array so that's why I cannot refer to bottles[1].waterAmount.

mikro098
  • 2,173
  • 2
  • 32
  • 48
  • 1
    you will be surprised, but answer is here http://stackoverflow.com/questions/40480/is-java-pass-by-reference-or-pass-by-value – Iłya Bursov Jan 12 '17 at 19:17
  • 1
    Possible duplicate of [What is a NullPointerException, and how do I fix it?](http://stackoverflow.com/questions/218384/what-is-a-nullpointerexception-and-how-do-i-fix-it) – Youcef LAIDANI Jan 12 '17 at 19:18
  • 2
    Updating the local variable `bottle` inside your foreach loop will in no way modify the contents of the array from which that variable was initiatlized. – azurefrog Jan 12 '17 at 19:18
  • 2
    Related: [Why JAVA foreach doesn't change element value?](https://stackoverflow.com/questions/15844443/why-java-foreach-doesnt-change-element-value) – Pshemo Jan 12 '17 at 19:19
  • @azurefrog that's exactly what I'm asking about. So is there any way to use foreach loop to work like for loop in the case? – mikro098 Jan 12 '17 at 19:19
  • @Pshemo that's what I was looking for but after couple of threads I've decided to post my own question. Thanks for sharing! – mikro098 Jan 12 '17 at 19:21
  • If you want to modify the array directly, see: [Is there a way to access an iteration-counter in Java's for-each loop?](http://stackoverflow.com/questions/477550/is-there-a-way-to-access-an-iteration-counter-in-javas-for-each-loop) – azurefrog Jan 12 '17 at 19:23
  • @azurefrog ok, so the simplest and most common method is default for loop like the one I've posted in my code? – mikro098 Jan 12 '17 at 19:23
  • You could use foreach to ensure amount of iterations, but to update content of array you would need `array[index]=...`. So if we write `int index=0; for(Type element: array){array[index++]=...;}` we are using foreach so technically we could say that it is possible to initialize array with foreach, but in reality simple for is better for that task. You could also take a look at [`Arrays.setAll(array, elementGenerator)`](https://docs.oracle.com/javase/8/docs/api/java/util/Arrays.html#setAll-T:A-java.util.function.IntFunction-). – Pshemo Jan 12 '17 at 19:26

2 Answers2

2

Yes, you can do it, but if I saw that particular construct I'd want to know why it was done that way instead of just using the traditional for loop.

Try:

   Bottle[] bottles = new Bottle[3];
   int counter = 0;

   //foreach
   for (Bottle bottle : bottles) {
        bottles[counter] = new Bottle();
        System.out.println(bottles[counter].waterAmount);
        counter++;
   }
Sandy Simonton
  • 604
  • 5
  • 15
  • 1
    I thought that foreach is better option but as I can see it can create some problems and standard for loop is super easy in this case. So you to use for loop in this case? – mikro098 Jan 12 '17 at 19:29
  • I'd say yes, the traditional for loop is the best. My reasoning is that you wanted to make use of a counter as an index. foreach, in my opinion, is more suited to collections than it is to arrays. – Sandy Simonton Jan 12 '17 at 19:31
  • 1
    Although this code works, IMO it's not very readable: the `bottle` variable is not used and may generate a warning (_The value of the local variable bottle is not used_). – Robert Hume Jan 12 '17 at 19:31
  • 1
    Yup... To answer the question properly you have to write it this way even though it's not really good code. In the code I gave, bottle is just used as an artifact of the foreach loop to iterate through the indexes of the array. – Sandy Simonton Jan 12 '17 at 19:33
  • @RobertHume yes, I've noticed it too. What's the point of creating bottle if it's not used inside foreach loop. – mikro098 Jan 12 '17 at 19:34
  • 1
    Actually, you're not creating bottle per se... if you look at the array without assigning values to the indexes of the array, bottle is null every time. Bottle ends up being a reference to null upon each iteration. Behind the scenes you are allocating memory space in case bottle needs to point to an actual Bottle(), but that's trivial. – Sandy Simonton Jan 12 '17 at 19:35
1

You can't.

Actually you said it yourself in your code: for (Bottle bottle : bottles). You're creating a new variable, bottle, of type Bottle, to which foreach assigns current iteration. Changing the local variable (i.e. assigning new value using new) will have no effect, leaving you nulls in original array.

To address the other answer: that's not really using foreach, it's merely abusing it for iteration purposes; a simple while would have worked as well.

Luke
  • 1,284
  • 1
  • 12
  • 34
  • Question was, "[is it] possible to construct objects that are inside an array using foreach loop?" The answer is yes, it is, but it isn't "proper." The answer IS actually using a foreach, there's no bones about it. It compiles, runs, and achieves the same effect as the traditional for loop. – Sandy Simonton Jan 12 '17 at 19:38
  • Literally yes, semantically not really. It's a nice trick if you're left in a world without while and regular for loop, but 1) you're addressing the array from inside the loop and 2) you're using a counter. It's a legit answer, but there isn't a situation where that solution would make sense. – Luke Jan 12 '17 at 19:42
  • I know what it does, and yes, it is a legit answer. Everything that you said I mentioned in my answer and in the comments below it. What are you trying to achieve here? If you don't like the question, downvote the question, not the solution. – Sandy Simonton Jan 12 '17 at 19:42
  • 1
    I'm just stating that assigning variable from inside foreach isn't really possible due to the nature of foreach. It's not meant for it. It uses Iterators underneath and some black magic to address arrays, neither of which allows changing references to objects inside Iterable (or the array) Sure tricks are possible, but not applicable on e.g. set. What you wrote is actually a while loop (semantically) masked as foreach. I didn't downvote anything though, just got downvoted. – Luke Jan 12 '17 at 19:49