web-dev-qa-db-fra.com

Différence entre final et effectivement final

Je joue avec lambdas dans Java 8 et je suis tombé sur un avertissement local variables referenced from a lambda expression must be final or effectively final. Je sais que lorsque j'utilise des variables dans une classe anonyme, elles doivent être finales dans une classe externe, mais quelle est la différence entre final et effectivement final ?

292
alex

... à partir de Java SE 8, une classe locale peut accéder aux variables et paramètres locaux du bloc englobant qui sont finaux ou effectivement finaux. Une variable ou un paramètre dont la valeur n'est jamais modifiée après son initialisation est effectivement final.

Par exemple, supposons que la variable numberLength ne soit pas déclarée finale et que vous ajoutiez l'instruction d'affectation marquée dans le constructeur PhoneNumber:

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

  ...

  }

...

}

En raison de cette instruction d'affectation, la variable numberLength n'est plus effective. En conséquence, le compilateur Java génère un message d'erreur semblable à "Les variables locales référencées à partir d'une classe interne doivent être finales ou réellement définitives", la classe interne PhoneNumber essayant d'accéder à la variable numberLength:

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

http://docs.Oracle.com/javase/tutorial/Java/javaOO/localclasses.html

199
Suresh Atta

Je trouve que le moyen le plus simple d'expliquer «effectivement final» consiste à imaginer l'ajout du modificateur final à une déclaration de variable. Si, avec ce changement, le programme continue à se comporter de la même manière, à la fois au moment de la compilation et au moment de l'exécution, cette variable est effectivement finale.

115
Maurice Naftalin

Selon les docs :

Une variable ou un paramètre dont la valeur n'est jamais modifiée après son initialisation est effectivement final.

Fondamentalement, si le compilateur découvre qu'une variable n'apparaît pas dans les assignations en dehors de son initialisation, la variable est considérée comme effectivement finale.

Par exemple, considérons une classe:

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;
    }
}
32
Mark Elliot

Extrait d'un article de 'Brian Goetz',

'Effectivement final' est une variable qui ne donnerait pas d'erreur de compilation si elle devait être ajoutée par 'final'

lambda-state-final- Brian Goetz

25
Ajeet Ganga

La variable ci-dessous est final, nous ne pouvons donc pas changer sa valeur une fois initialisée. Si nous essayons, nous aurons une erreur de compilation ...

final int variable = 123;

Mais si nous créons une variable comme celle-ci, nous pouvons changer sa valeur ...

int variable = 123;
variable = 456;

Mais dans Java 8, toutes les variables sont final par défaut. Mais l’existence de la 2ème ligne du code le rend non final. Donc, si nous supprimons la 2ème ligne du code ci-dessus, notre variable est maintenant "effectivement finale" ...

int variable = 123;

Donc .. Toute variable affectée une fois et une seule fois est "effectivement finale".

14
Eurig Jones

Lorsqu'une expression lambda utilise une variable locale affectée à partir de son espace inclus, il existe une restriction importante. Une expression lambda ne peut utiliser que des variables locales dont la valeur ne change pas. Cette restriction est appelée " variable capture " qui est décrite comme; lambda expression capture les valeurs, pas les variables
Les variables locales qu'une expression lambda peut utiliser sont appelées " effectivement final ". 
Une variable effectivement finale est une variable dont la valeur ne change pas après sa première affectation. Il n'est pas nécessaire de déclarer explicitement une telle variable comme étant finale, bien que cela ne soit pas une erreur.
Voyons un exemple. Nous avons une variable locale i qui est initialisée avec la valeur 7; avec l'expression lambda, nous essayons de modifier cette valeur en affectant une nouvelle valeur à i. Cela entraînera une erreur du compilateur - "La variable locale i définie dans une portée englobante doit être finale ou effectivement finale"

@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;
        };
    }   
}
6
infoj

Une variable est final ou effectivement final quand elle est initialisée une fois et elle n'est jamais mutée dans sa classe de propriétaire. Et nous ne pouvons pas initialiser il dans boucles ou classes internes .

Finale :

final int number;
number = 23;

Effectivement final :

int number;
number = 34;
6
samadadi

La rubrique Effective finale est décrite dans JLS 4.12.4 et le dernier paragraphe consiste en une explication claire:

Si une variable est effectivement finale, l'ajout du modificateur final à sa déclaration n'introduira aucune erreur de compilation. Inversement, une variable ou un paramètre local déclaré final dans un programme valide devient effectivement final si le modificateur final est supprimé.

2
Dmitry N.
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; 

        }
    }    
}

Comme d'autres l'ont dit, une variable ou un paramètre dont la valeur n'est jamais modifiée après son initialisation est effectivement définitif. Dans le code ci-dessus, si vous modifiez la valeur de x dans la classe intérieure FirstLevel, le compilateur vous donnera le message d'erreur suivant:

Les variables locales référencées à partir d'une expression lambda doivent être finales ou effectivement définitives.

1
Girish

final est une variable déclarer avec la clé Word final, exemple: 

final double pi = 3.14 ;

il reste final tout au long du programme. 

effectivement final : toute variable ou paramètre local auquel une valeur est affectée une seule fois à l'heure actuelle (ou qui n'est mise à jour qu'une fois). Il ne peut pas rester effectivement définitif durant le programme. cela signifie donc que effectivement final variable pourrait perdre sa propriété effectivement finale après le moment où elle est assignée/mise à jour au moins une autre affectation. Exemple:

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

Si vous pouviez ajouter le modificateur final à une variable locale, c’était effectivement final.

Les expressions Lambda peuvent accéder 

  • variables statiques, 

  • variables d'instance,

  • paramètres de méthode effectivement définitifs, et 

  • effectivement final variables locales.

Source: OCP: Guide d'étude du programmeur II pour Java SE 8, certifié par Oracle, Jeanne Boyarsky, Scott Selikoff

Aditionellement, 

Une variable effectively final est une variable dont la valeur n'est jamais changé, mais il n’est pas déclaré avec le mot clé final.

Source: Démarrer avec Java: des structures de contrôle à travers les objets (6ème édition), Tony Gaddis

De plus, n'oubliez pas la signification de final, à savoir qu'il est initialisé exactement une fois avant sa première utilisation.

1
snr

Déclarer une variable final ou ne pas la déclarer final, mais le conserver effectivement final peut donner lieu (dépend du compilateur) à un autre bytecode.

Regardons un petit exemple:

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

Le bytecode correspondant à la méthode main (Java 8u161 sur Windows 64 bits):

  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

Le tableau de numéro de ligne correspondant:

 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

Comme nous voyons le code source aux lignes 12, 13, 14 ne figure pas dans le code octet. C'est parce que i est true et ne changera pas son état. Ainsi, ce code est inaccessible (plus dans cette réponse ). Pour la même raison, le code à la ligne 9 manque également. L'état de i ne doit pas nécessairement être évalué puisqu'il est true avec certitude.

D'autre part, bien que la variable j soit effectivement finale, elle n'est pas traitée de la même manière. Aucune optimisation de ce type n’est appliquée. L'état de j est évalué deux fois. Le bytecode est le même, peu importe que j soit effectivement final.

0
LuCio