Difference between final and effectively final

后端 未结 14 2822
孤独总比滥情好
孤独总比滥情好 2020-11-22 00:38

I\'m playing with lambdas in Java 8 and I came across warning local variables referenced from a lambda expression must be final or effectively final. I know tha

相关标签:
14条回答
  • 2020-11-22 00:47

    Declaring a variable final or not declaring it final, but keeping it effectively final may result (depends on compiler) in different bytecode.

    Let's have a look on a small example:

        public static void main(String[] args) {
            final boolean i = true;   // 6  // final by declaration
            boolean j = true;         // 7  // effectively final
    
            if (i) {                  // 9
                System.out.println(i);// 10
            }
            if (!i) {                 // 12
                System.out.println(i);// 13
            }
            if (j) {                  // 15
                System.out.println(j);// 16
            }
            if (!j) {                 // 18
                System.out.println(j);// 19
            }
        }
    

    The corresponding bytecode of the main method (Java 8u161 on Windows 64 Bit):

      public static void main(java.lang.String[]);
        Code:
           0: iconst_1
           1: istore_1
           2: iconst_1
           3: istore_2
           4: getstatic     #16                 // Field java/lang/System.out:Ljava/io/PrintStream;
           7: iconst_1
           8: invokevirtual #22                 // Method java/io/PrintStream.println:(Z)V
          11: iload_2
          12: ifeq          22
          15: getstatic     #16                 // Field java/lang/System.out:Ljava/io/PrintStream;
          18: iload_2
          19: invokevirtual #22                 // Method java/io/PrintStream.println:(Z)V
          22: iload_2
          23: ifne          33
          26: getstatic     #16                 // Field java/lang/System.out:Ljava/io/PrintStream;
          29: iload_2
          30: invokevirtual #22                 // Method java/io/PrintStream.println:(Z)V
          33: return
    

    The corresponding line number table:

     LineNumberTable:
       line 6: 0
       line 7: 2
       line 10: 4
       line 15: 11
       line 16: 15
       line 18: 22
       line 19: 26
       line 21: 33
    

    As we see the source code at lines 12, 13, 14 doesn't appear in the byte code. That's because i is true and will not change it's state. Thus this code is unreachable (more in this answer). For the same reason the code at line 9 misses too. The state of i doesn't have to be evaluated since it is true for sure.

    On the other hand though the variable j is effectively final it's not processed in the same way. There are no such optimizations applied. The state of j is evaluated two times. The bytecode is the same regardless of j being effectively final.

    0 讨论(0)
  • 2020-11-22 00:53

    I find the simplest way to explain "effectively final" is to imagine adding the final modifier to a variable declaration. If, with this change, the program continues to behave in the same way, both at compile time and at run time, then that variable is effectively final.

    0 讨论(0)
  • 2020-11-22 00:54

    This variable below is final, so we can't change it's value once initialised. If we try to we'll get a compilation error...

    final int variable = 123;
    

    But if we create a variable like this, we can change it's value...

    int variable = 123;
    variable = 456;
    

    But in Java 8, all variables are final by default. But the existence of the 2nd line in the code makes it non-final. So if we remove the 2nd line from the above code, our variable is now "effectively final"...

    int variable = 123;
    

    So.. Any variable that is assigned once and only once, is "effectively final".

    0 讨论(0)
  • 2020-11-22 00:59

    A variable is final or effectively final when it's initialized once and it's never mutated in its owner class. And we can't initialize it in loops or inner classes.

    Final:

    final int number;
    number = 23;
    

    Effectively Final:

    int number;
    number = 34;
    

    Note: Final and Effective Final are similar(Their value don't change after assignment) but just that effective Final variables are not declared with Keyword final.

    0 讨论(0)
  • 2020-11-22 01:00

    When a lambda expression uses an assigned local variable from its enclosing space there is an important restriction. A lambda expression may only use local variable whose value doesn't change. That restriction is referred as "variable capture" which is described as; lambda expression capture values, not variables.
    The local variables that a lambda expression may use are known as "effectively final".
    An effectively final variable is one whose value does not change after it is first assigned. There is no need to explicitly declare such a variable as final, although doing so would not be an error.
    Let's see it with an example, we have a local variable i which is initialized with the value 7, with in the lambda expression we are trying to change that value by assigning a new value to i. This will result in compiler error - "Local variable i defined in an enclosing scope must be final or effectively final"

    @FunctionalInterface
    interface IFuncInt {
        int func(int num1, int num2);
        public String toString();
    }
    
    public class LambdaVarDemo {
    
        public static void main(String[] args){             
            int i = 7;
            IFuncInt funcInt = (num1, num2) -> {
                i = num1 + num2;
                return i;
            };
        }   
    }
    
    0 讨论(0)
  • 2020-11-22 01:01

    The Effectively final variable is a local variable that is:

    1. Not defined as final
    2. Assigned to ONLY once.

    While a final variable is a variable that is:

    1. declared with a final keyword.
    0 讨论(0)
提交回复
热议问题