2

I'm guessing it does not work, because in the syntax the first parameter must have a declared type. But what if I'm using a for-each loop in a function with an argument like I am in this example? The variable p has a declared type, I just want to reuse it.

public void addItem(Product p) {
  // ensure that we don't add any nulls to the item list
  if (p != null ) {
    int i = 0;

    for (p : items.keySet()) {
      i++;
    }

    items.put( p , i);
  }
}

There is a related question, Java for loop syntax: "for (T obj : objects)". That one asks what the for-each syntax means. I know what it means, I just want to reuse an existing variable rather than having to declare a new one.

Community
  • 1
  • 1
recurf
  • 237
  • 4
  • 16

3 Answers3

7

The Java Language Specification (the JLS) requires that you declare a local variable for the iterator.

i.e. you need

for (Product p : items.keySet()){

You can't borrow p from the outer scope. One way round your predicament is to use something like

for (Product inner_p : items.keySet()){`
    p = inner_p;
    /* continue as before*/

But this circumvents the intention of the JLS, so perhaps restructuring your code to a more elegant form might be better.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
  • In case you like to link to the JLS: https://docs.oracle.com/javase/specs/jls/se8/html/jls-14.html#jls-14.14.2 – Tom Mar 04 '16 at 13:56
  • So there is absolutely no way to use predefined variables? That seems silly – recurf Mar 04 '16 at 14:03
1

You need to prepend the type of the object iterated (the parametrized type of your collection) prior to its name.

See docs here (thanks Tom).

So: for([type] p : items.keySet())

For instance, if the keys of your Map are of your custom type Product:

for(Product p : items.keySet())

Also, you can spare yourself the iteration and set i as items.keySet().size() in this instance.

Community
  • 1
  • 1
Mena
  • 47,782
  • 11
  • 87
  • 106
  • Yes I know, but my type is already defined in the method constructor so i cannot give it another definition this is syntactically incorrect. – recurf Mar 04 '16 at 13:55
  • 1
    Mena, in case you like to link to the JLS: https://docs.oracle.com/javase/specs/jls/se8/html/jls-14.html#jls-14.14.2 – Tom Mar 04 '16 at 13:56
  • @JoshuaofX you need to syntactically specify the type of your for-each iteration. Also, there is no such thing as a "method constructor". Check the key type of your `items` map and use that. – Mena Mar 04 '16 at 13:58
1

I'd like to restructure the code a bit to demonstrate what your intended syntax might mean. In other words, the following code is functionally equivalent to what your code would do, were it possible to say it that way.

public void addItem(Product p) {
  // ensure that we don't add any nulls to the item list
  if (p != null ) {
    int i = 0;
    Product theProductIWantToInsert = p;

    for (Product p1 : items.keySet()) {
      theProductIWantToInsert = p1;
      i++;
    }

    items.put( theProductIWantToInsert, i);
  }
}

If you could reuse p as you suggest, it would take on all of the values in items, one by one. At the end of the for loop, p would be holding the value of the last Product that was already in the items list, not the value that you passed in to the addItem() method. The only time that p after the for loop would hold the value of p before the for loop would be when items was empty.

Ultimately, you'd only ever have 0 or 1 values in items. You'd have 0 before you invoked addItem the first time, then you'd have 1 forever after, and that 1 would be the first Product you passed to addItem().

So completely apart from the idea expressed in other answers, that the JLS just doesn't allow this, I want you to understand the implications for your code of doing the thing that you seem to want to do. It would be bad.

Erick G. Hagstrom
  • 4,873
  • 1
  • 24
  • 38
  • Hello Erick, Yes you are completely correct. The code is mostly just an example to show my error in the for each loop. – recurf Mar 04 '16 at 14:13