Enhanced 'for' loop and lambda expressions

后端 未结 4 1477
感情败类
感情败类 2020-12-05 04:41

To my understanding, lambda expressions capture values, not the variables. For example, the following is a compile-time error:

for (int k = 0; k < 10; k++         


        
4条回答
  •  难免孤独
    2020-12-05 04:50

    In an enhanced for loop the variable is initialized every iteration. From §14.14.2 of the Java Language Specification (JLS):

    ...

    When an enhanced for statement is executed, the local variable is initialized, on each iteration of the loop, to successive elements of the array or Iterable produced by the expression. The precise meaning of the enhanced for statement is given by translation into a basic for statement, as follows:

    • If the type of Expression is a subtype of Iterable, then the translation is as follows.

      If the type of Expression is a subtype of Iterable for some type argument X, then let I be the type java.util.Iterator; otherwise, let I be the raw type java.util.Iterator.

      The enhanced for statement is equivalent to a basic for statement of the form:

      for (I #i = Expression.iterator(); #i.hasNext(); ) {
          {VariableModifier} TargetType Identifier =
              (TargetType) #i.next();
          Statement
      }
      

    ...

    • Otherwise, the Expression necessarily has an array type, T[].

      Let L1 ... Lm be the (possibly empty) sequence of labels immediately preceding the enhanced for statement.

      The enhanced for statement is equivalent to a basic for statement of the form:

      T[] #a = Expression;
      L1: L2: ... Lm:
      for (int #i = 0; #i < #a.length; #i++) {
          {VariableModifier} TargetType Identifier = #a[#i];
          Statement
      }
      

    ...

    In other words, your enhanced for loop is equivalent to:

    ArrayList listOfInt = new ArrayList<>();
    // add elements...
    
    for (Iterator itr = listOfInt.iterator(); itr.hasNext(); ) {
        Integer arg = itr.next();
        new Thread(() -> System.out.println(arg)).start();
    }
    

    Since the variable is initialized each iteration it is effectively final (unless you modify the variable inside the loop).

    In contrast, the variable in the basic for loop (k in your case) is initialized once and updated each iteration (if a "ForUpdate" is present, e.g. k++). See §14.14.1 of the JLS for more information. Since the variable is updated each iteration is is not final nor effectively final.

    The need for a final or effectively final variable is mandated and explained by §15.27.2 of the JLS:

    ...

    Any local variable, formal parameter, or exception parameter used but not declared in a lambda expression must either be declared final or be effectively final (§4.12.4), or a compile-time error occurs where the use is attempted.

    Any local variable used but not declared in a lambda body must be definitely assigned (§16 (Definite Assignment)) before the lambda body, or a compile-time error occurs.

    Similar rules on variable use apply in the body of an inner class (§8.1.3). The restriction to effectively final variables prohibits access to dynamically-changing local variables, whose capture would likely introduce concurrency problems. Compared to the final restriction, it reduces the clerical burden on programmers.

    The restriction to effectively final variables includes standard loop variables, but not enhanced-for loop variables, which are treated as distinct for each iteration of the loop (§14.14.2).

    ...

    That last sentence even explicitly mentions the difference between basic for loop variables and enhanced for loop variables.

提交回复
热议问题