3

Following compiles without any errors:

class App {
    boolean b;
    boolean c;

    void foo(List<Integer> ints) {
        myLabel:
        for (Integer i : ints) {
            while (!b) {
                if (c) {
                    continue myLabel;
                }
            }
        };
    }
}

But if I modify foo as follows:

void foo(List<Integer> ints) {
    myLabel:
    ints.forEach(integer -> {
        while (!b) {
            if (c) {
                continue myLabel;
            }
        }
    });
}

I get Error:(17, 21) undefined label: myLabel

What is the difference? As far as I know, the new forEach is just a shortcut for the enhanced for loop?

Koray Tugay
  • 22,894
  • 45
  • 188
  • 319
  • 4
    Because you're in a separate (lambda) function. – SLaks Feb 13 '19 at 03:10
  • 1
    *" the new forEach is just a shortcut for the enhanced for loop?"* - No, not really, it's a method which takes a closure which can perform an operation on the element which is passed to it – MadProgrammer Feb 13 '19 at 03:10
  • @SLaks but you can access variables outside lambda. why not a label? I wonder if the JLS specifies this – Eugene Feb 13 '19 at 03:11
  • I actually tried `final myLabel` thinking the same thing, which obviously itself a syntax error. @Eugene – Koray Tugay Feb 13 '19 at 03:12
  • Syntactically it's completely different, it's just a method invocation. Labels are meant to work with loops not method invocations. – Sotirios Delimanolis Feb 13 '19 at 03:16
  • @Eugene: Because they're captured in a closure. That has nothing to do with control flow; how would it even make sense to jump across functions? What if the lambda is called later? – SLaks Feb 13 '19 at 03:29
  • @SLaks yeah... I realized that after posting the comment – Eugene Feb 13 '19 at 03:30
  • Note that you can achieve the desired result with the lambda expression, when you replace `continue myLabel;` with `return;`. Control flow statements within a lambda expression are best understood when thinking of the lambda body as some kind of nested *method*. – Holger Feb 13 '19 at 08:46
  • @Holger Very useful information, thank you for commenting. – Koray Tugay Feb 13 '19 at 15:18

1 Answers1

5

As stated in the comments, forEach is just a method invocation. The snippet

myLabel: ints.forEach(integer -> ...);

is a labeled statement:

identifier statement labels are used with break or continue statements (§14.15, §14.16) appearing anywhere within the labeled statement.

To repeat, the labeled statement is the method invocation expression. Your continue statement is not within the labeled statement.

Your continue statement is within a while statement appearing within the body of a lambda expression.

A continue statement with label Identifier attempts to transfer control to the enclosing labeled statement (§14.7) that has the same Identifier as its label; that statement, which is called the continue target, then immediately ends the current iteration and begins a new one.

[...]

The continue target must be a while, do, or for statement, or a compile-time error occurs.

A continue statement must refer to a label within the immediately enclosing method, constructor, initializer, or lambda body. There are no non-local jumps. If no labeled statement with Identifier as its label in the immediately enclosing method, constructor, initializer, or lambda body contains the continue statement, a compile-time error occurs.

Since there is no labeled (while, do, or for) statement named myLabel in the immediately enclosing lambda body, you get a compile-time error.

Savior
  • 3,225
  • 4
  • 24
  • 48