382 votes

Différence entre final et effectivement final

Je joue avec les lambdas en 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 à l'intérieur d'une classe anonyme, elles doivent être finales dans la classe externe, mais quand même - quelle est la différence entre final y effectivement final ?

2 votes

Beaucoup de réponses, mais toutes se résument essentiellement à "aucune différence". Mais est-ce vraiment vrai ? Malheureusement, je n'arrive pas à trouver une spécification de langage pour Java 8.

3 votes

@AleksandrDubinsky docs.oracle.com/javase/specs

1 votes

@AleksandrDubinsky pas "vraiment" vrai. J'ai trouvé une exception à cette règle. Une variable locale initialisée avec une constante n'est pas une expression constante pour le compilateur. Vous ne pouvez pas utiliser une telle variable pour un cas dans un switch/case avant d'ajouter explicitement le mot-clé final. Par exemple : "int k = 1 ; switch(someInt) { case k : ...".

248voto

... à partir de Java SE 8, une classe locale peut accéder aux variables locales et aux paramètres 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 finale.

Par exemple, supposons que la variable numberLength n'est pas déclaré final, et vous ajoutez l'instruction d'affectation marquée dans le fichier PhoneNumber constructeur :

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

  ...

  }

...

}

Grâce à cette instruction d'affectation, la variable numberLength n'est plus effectivement finale. En conséquence, le compilateur Java génère un message d'erreur similaire à "les variables locales référencées depuis une classe interne doivent être finales ou effectivement finales". où la classe interne PhoneNumber tente 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

77 votes

+1 Note : si une référence n'est pas modifiée, elle est effectivement définitive, même si l'objet référencé est modifié.

0 votes

@SURESH ATTA Pourquoi une classe locale ne peut-elle pas accéder à une variable finale effective d'un bloc englobant ? Existe-t-il un moyen de modifier la variable (qui est une référence ou une variable primitive) dans la classe locale, si c'est le cas, ce n'est plus une variable effectivement finale.

1 votes

@stanleyerror Cela pourrait vous aider : stackoverflow.com/questions/4732544/

142voto

Maurice Naftalin Points 1718

Je trouve que la façon la plus simple d'expliquer "effectivement final" est d'imaginer ajouter le final à une déclaration de variable. Si, avec cette modification, le programme continue à se comporter de la même manière, tant à la compilation qu'à l'exécution, alors cette variable est effectivement finale.

4 votes

C'est vrai, tant que l'on comprend bien la notion de "final" de java 8. Sinon, je regarderais une variable non déclarée finale à laquelle vous feriez une affectation plus tard, et je penserais à tort qu'elle n'est pas finale. Vous pourriez dire "bien sûr"... mais tout le monde ne prête pas autant d'attention qu'il le devrait aux derniers changements de version du langage.

9 votes

Une exception à cette règle est qu'une variable locale initialisée avec une constante n'est pas une expression constante pour le compilateur. Vous ne pouvez pas utiliser une telle variable pour un cas dans un switch/case tant que vous n'avez pas explicitement ajouté le mot-clé final. Par exemple : "int k = 1 ; switch(someInt) { case k : ...".

2 votes

@HennoVermeulen switch-case n'est pas une exception à la règle dans cette réponse. La langue spécifie que case k nécessite un expression constante qui pourrait être un variable constante ("Une variable constante est une variable finale de type primitif ou de type String qui est initialisée avec une expression constante". JLS 4.12.4 ) qui est un cas particulier de variable finale.

39voto

Mark Elliot Points 31871

Selon le docs :

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

Fondamentalement, si le compilateur constate qu'une variable n'apparaît pas dans les affectations en dehors de son initialisation, alors la variable est considérée comme effectivement final .

Par exemple, considérons une certaine 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;
    }
}

0 votes

La documentation parle de variables locales. bar dans votre exemple n'est pas une variable locale, mais un champ. L'expression "Effectivement définitif" figurant dans le message d'erreur ci-dessus ne s'applique pas du tout aux champs.

6 votes

@AnttiHaapala bar est un paramètre ici, pas un champ.

1voto

Tenacious Points 1124
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 finale. Dans le code ci-dessus, si vous changez la valeur de x dans la classe intérieure FirstLevel alors le compilateur vous donnera le message d'erreur :

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

-6voto

FiruzzZ Points 31

Cependant, à partir de Java SE 8, une classe locale peut accéder aux variables locales et aux paramètres du bloc >enclos qui sont finaux ou effectivement finaux.

Cela n'a pas commencé avec Java 8, je l'utilise depuis longtemps. Ce code était (avant java 8) légal :

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

2 votes

Cette déclaration fait référence au fait que dans <Java 8, sólo final sont accessibles, mais en Java 8 également ceux qui sont efficacement finale.

0 votes

Je ne vois que du code qui ne fonctionne pas, que vous utilisiez Java 7 ou Java 8.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X