ChatGPT解决这个技术问题 Extra ChatGPT

Difference between final and effectively final

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 that when I use variables inside anonymous class they must be final in outer class, but still - what is the difference between final and effectively final?

Lots of answers, yet all essentially amount to "no difference." But is that really true? Unfortunately, I can't seem to find a Language Specification for Java 8.
@AleksandrDubinsky docs.oracle.com/javase/specs
@AleksandrDubinsky not "really" true. I found one exception to this rule. A local variable initialized with a constant is not a constant expression to the compiler. You cannot use such a variable for a case in a switch/case until you explicitly add the final keyword. E.g. "int k = 1; switch(someInt) { case k: ...".

S
Suresh Atta

... starting in Java SE 8, a local class can access local variables and parameters of the enclosing block that are final or effectively final. A variable or parameter whose value is never changed after it is initialized is effectively final.

For example, suppose that the variable numberLength is not declared final, and you add the marked assignment statement in the PhoneNumber constructor:

public class OutterClass {  

  int numberLength; // <== not *final*

  class PhoneNumber {

    PhoneNumber(String phoneNumber) {
        numberLength = 7;   // <== assignment to numberLength
        String currentNumber = phoneNumber.replaceAll(
            regularExpression, "");
        if (currentNumber.length() == numberLength)
            formattedPhoneNumber = currentNumber;
        else
            formattedPhoneNumber = null;
     }

  ...

  }

...

}

Because of this assignment statement, the variable numberLength is not effectively final anymore. As a result, the Java compiler generates an error message similar to "local variables referenced from an inner class must be final or effectively final" where the inner class PhoneNumber tries to access the numberLength variable:

http://codeinventions.blogspot.in/2014/07/difference-between-final-and.html

http://docs.oracle.com/javase/tutorial/java/javaOO/localclasses.html


+1 Note: if a reference is not changed it is effectively final even if the object referenced is changed.
@stanleyerror This might be of some help: stackoverflow.com/questions/4732544/…
I think more useful than an example of not effectively final, is an example of when something is effectively final. Though the description does make it clear. Var need not be declared final if no code changes it's value.
The example is incorrect. This code perfectly compiles (without dots, of course). To get the compiler error this code should be inside some method so that numberLength becomes a local variable of this method.
Is there a reason why this example is so complicated? Why is the majority of the code dealing with an entirely irrelevant regex operation? And, as @mykola already said, it’s completely missing the mark regarding the effective final property, as that is only relevant to local variables and there is no local variable in this example.
M
Maurice Naftalin

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.


This is true, as long as the understanding of java 8's "final" is well understood. Otherwise I'd look at a variable not declared final that you made an assignment to later, and erroneously think it wasn't final. You might say "of course"... but not everybody pays as much attention to the latest language version changes as they ought.
One exception to this rule is that a local variable initialized with a constant is not a constant expression to the compiler. You cannot use such a variable for a case in a switch/case until you explicitly add the final keyword. E.g. "int k = 1; switch(someInt) { case k: ...".
@HennoVermeulen switch-case is not an exception to the rule in this answer. The language specifies that case k requires a constant expression which could be a constant variable ("A constant variable is a final variable of primitive type or type String that is initialized with a constant expression" JLS 4.12.4) which is a special case of a final variable.
In my example the compiler complains that k is not a constant expression so it cannot be used for the switch. When adding final, compilation behavior changes because it is now a constant variable and it can be used in the switch. So you are right: the rule is still correct. It simply does not apply to this example and does not say whether k is effectively final or not.
E
Eurig Jones

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".


As simple as the answer should be.
@Eurig, Citation required for "all variables are final by default".
Why are they then final by default when we can change their value easily and "overwrite" effectively final concept?
5
5gon12eder

According to the docs:

A variable or parameter whose value is never changed after it is initialized is effectively final.

Basically, if the compiler finds a variable does not appear in assignments outside of its initialization, then the variable is considered effectively final.

For example, consider some class:

public class Foo {

    public void baz(int bar) {
        // While the next line is commented, bar is effectively final
        // and while it is uncommented, the assignment means it is not
        // effectively final.

        // bar = 2;
    }
}

The docs talk about local variables. bar in your example is not a local variaable, but a field. "Effectively final" in the error message as above does not apply to fields at all.
@AnttiHaapala bar is a parameter here, not a field.
A
AndrewF

'Effectively final' is a variable which would not give compiler error if it were to be appended by 'final'

From a article by 'Brian Goetz',

Informally, a local variable is effectively final if its initial value is never changed -- in other words, declaring it final would not cause a compilation failure.

lambda-state-final- Brian Goetz


this answer is shown as a quote, however there is no such exact text in Brian's article, for sure not the word appended. This is a quote instead: Informally, a local variable is effectively final if its initial value is never changed -- in other words, declaring it final would not cause a compilation failure.
From the article verbatim copy: Informally, a local variable is effectively final if its initial value is never changed -- in other words, declaring it final would not cause a compilation failure.
V
Vishwa Ratna

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.


B
Brad Larson

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;
        };
    }   
}

N
Novdar

Effective final topic is described in JLS 4.12.4 and the last paragraph consists a clear explanation:

If a variable is effectively final, adding the final modifier to its declaration will not introduce any compile-time errors. Conversely, a local variable or parameter that is declared final in a valid program becomes effectively final if the final modifier is removed.


T
The Scientific Method

final is a variable declare with key word final , example:

final double pi = 3.14 ;

it remains final through out the program, changing pi after this line is never allowed.

effectively final : any local variable or parameter that is assigned a value only once right now(or updated only once). It may not remain effectively final throughout the program. so this means that effectively final variable might loses its effectively final property after immediately the time it gets assigned/updated at least one more assignment. example:

class EffectivelyFinal {
    
    public static void main(String[] args) {
        calculate(124,53);
    }
    
    public static void calculate( int operand1, int operand2){   
     int rem = 0;  //   operand1, operand2 and rem are effectively final here
     rem = operand1%2  // rem lost its effectively final property here because it gets its second assignment 
                       // operand1, operand2 are still effectively final here 
        class operators{

            void setNum(){
                operand1 =   operand2%2;  // operand1 lost its effectively final property here because it gets its second assignment
            }
            
            int add(){
                return rem + operand2;  // does not compile because rem is not effectively final
            }
            int multiply(){
                return rem * operand1;  // does not compile because both rem and operand1 are not effectively final
            }
        }   
   }    
}

This is incorrect according to the Java Language Specification: "Whenever it occurs as the left hand side in an assignment expression, it is definitely unassigned and not definitely assigned before the assignment." A variable/parameter is either always or never effectively final. More explicitly, if you cannot add the final keyword to a declaration without introducing compile errors, then it is not effectively final. It is the contrapositive of this statement: "If a variable is effectively final, adding the final modifier to its declaration will not introduce any compile-time errors."
The comments in the example code are incorrect for all the reasons described in my comment. "Effectively final" is not a state that can change over time.
@AndrewF if it does not change over time, what do you think the last line does not compile? rem was effectively final on line 1 in calculate method . However, on the last line, the compiler complains that rem is not effectively final
You are correct that some of the code needs to be removed from your code block in order to compile, but that doesn't reflect runtime behavior. At compile time, you can decide whether a variable is effectively final or not -- based on the spec, it is either always effectively final, or it is never effectively final. The compiler can tell by statically looking at how the variable is used throughout its scope. The property cannot be gained or lost as the program runs. The term is well-defined by the spec -- check out the other answers, which explain it pretty well.
d
dimo414
public class LambdaScopeTest {
    public int x = 0;        
    class FirstLevel {
        public int x = 1;    
        void methodInFirstLevel(int x) {

            // The following statement causes the compiler to generate
            // the error "local variables referenced from a lambda expression
            // must be final or effectively final" in statement A:
            //
            // x = 99; 

        }
    }    
}

As others have said, a variable or parameter whose value is never changed after it is initialized is effectively final. In the above code, if you change the value of x in inner class FirstLevel then the compiler will give you the error message:

Local variables referenced from a lambda expression must be final or effectively final.


s
snr

If you could add the final modifier to a local variable, it was effectively final.

Lambda expressions can access

static variables,

instance variables,

effectively final method parameters, and

effectively final local variables.

Source: OCP: Oracle Certified Professional Java SE 8 Programmer II Study Guide, Jeanne Boyarsky, Scott Selikoff

Additionally,

An effectively final variable is a variable whose value is never changed, but it isn’t declared with the final keyword.

Source: Starting Out with Java: From Control Structures through Objects (6th Edition), Tony Gaddis

Furthermore, don't forget the meaning of final that it is initialized exactly once before it is used for the first time.


L
LuCio

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.


I would consider this a compiler inefficiency, and not necessarily one that would still be true in newer compilers. In a perfect compile, if a variable is effectively final then it will generate all the exact same optimizations as one declared final. So don't rely on the notion that effectively final is automatically slower than declaring something final.
@AndrewF Generally you're right, the behaviour may change. That's why I wrote "may result (depends on compiler) in different bytecode". Only because of the missing optimization (different bytecode) I wouldn't assume the execution to be slower. But it's still a difference in the case shown.
J
Jimmy_Rw

The Effectively final variable is a local variable that is:

Not defined as final Assigned to ONLY once.

While a final variable is a variable that is:

declared with a final keyword.


F
FiruzzZ

However, starting in Java SE 8, a local class can access local variables and parameters of the >enclosing block that are final or effectively final.

This didn't start on Java 8, I use this since long time. This code used (before java 8) to be legal:

String str = ""; //<-- not accesible from anonymous classes implementation
final String strFin = ""; //<-- accesible 
button.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
         String ann = str; // <---- error, must be final (IDE's gives the hint);
         String ann = strFin; // <---- legal;
         String str = "legal statement on java 7,"
                +"Java 8 doesn't allow this, it thinks that I'm trying to use the str declared before the anonymous impl."; 
         //we are forced to use another name than str
    }
);

The statement refers to the fact that in <Java 8, only final variables can be accessed, but in Java 8 also those that are effectively final.
I only see code which doesn’t work, regardless of whether you are using Java 7 or Java 8.