2017-10-05 7 views
0

Je comprends cela comme une partie standard de la programmation fonctionnelle .. ma question est de savoir pourquoi le compilateur ne peut pas automatiquement déclarer une copie de la variable comme finale juste avant le début de l'instruction lambda?Java: pourquoi java ne peut pas "finaliser" automatiquement les variables locales dans la portée englobante de l'expression lambda?

import java.util.stream.IntStream; 

public class Example 
{ 
    public static void main(String args[]) 
    { 
    int i = 5; 
    i = 6; 
    IntStream.range(0, 10).mapToLong(j-> i * j).sum(); 
    } 


} 

échoue ... avec « Variable locale i définie dans un champ englobante doit être définitive ou efficace finale » alors qu'il semble que le compilateur devrait être assez intelligent pour faire quelque chose comme ça

import java.util.stream.IntStream; 

public class Example 
{ 
    public static void main(String args[]) 
    { 
    int i = 5; 
    i = 6; 
    final int _i = i; 
    IntStream.range(0, 10).mapToLong(j-> _i * j).sum(); 
    } 


} 

la compilateur pourrait appliquer que la variable finalisée est jamais modifiée par la fonction lambda

+3

Il pourrait; cela rendrait le code plus difficile à raisonner. –

+0

Serait plus difficile à utiliser et à comprendre dans les scénarios multithread (qui bénéficient également de finales). –

+0

@AdamKotwasinski comment le multi-threading peut-il changer quelque chose? Je ne propose pas que la fonction lambda soit capable de modifier des variables dans sa portée parente. – crow

Répondre

0

Mais si le lambda était passé quelque part qui l'a utilisé de manière asynchrone (à-dire qu'elle peut courir après la fonction en cours se termine), et vous modifiez la i variable dans la portée de la fonction après la création du lambda?

int i = 5; 
i = 6; 
useLambdaAsynchronously(j-> i * j); 
i = 7; 

Le lambda aurait encore pris une valeur de 6 pour i, mais i (qui est censé être la même variable, parce que vous avez seulement déclaré un i) a maintenant la valeur de 7 dans un autre champ. Ceci est incohérent, car le programmeur doit s'attendre à ce qu'une seule variable ait une seule valeur à la fois. Si le lambda est exécuté plus tard, il utilisera toujours la valeur 6 pour i même si 7 a déjà été affecté à i précédemment.

Pour éviter ce problème, le compilateur doit s'assurer que la variable n'est pas affectée dans le lambda et la variable n'est pas affectée dans la portée de fonction d'origine après la création du lambda. Mais cela conduirait à la situation où une affectation plus tôt dans une fonction est permise mais plus tard dans la même fonction n'est pas autorisée (juste parce qu'elle a été capturée par un lambda), ce qui peut aussi surprendre le programmeur. Pour plus de simplicité, Java n'autorise pas les affectations où que ce soit.

0

Eh bien, le compilateur serait faire réellement si votre variable i étaient effectivement final.

public static void main(String args[]) 
    { 
    int i = 5; 
    IntStream.range(0, 10).mapToLong(j-> i * j).sum(); 
    } 

Cependant, avec la deuxième affectation i = 6; vous rendant pas « efficace finale », vous indiquez que vous voulez réellement être mutable.

Alors pourquoi dans ce cas le compilateur doit-il faire une copie finale de votre variable malgré que vous ayez indiqué que vous voulez qu'il soit modifiable?

+0

le compilateur devrait savoir que la variable n'est pas modifiée après l'expression lambda dans la portée englobante ni modifiée dans l'expression lambda et devrait donc générer les codes d'octets nécessaires pour que ma solution de contournement de ajouter "final int _a = a;" juste avant que l'expression lambda ne soit nécessaire. le code de la plaque de chaudière suce et il a été la direction des versions récentes de Java pour le rendre moins nécessaire d'utiliser des constructions verbose et coolplate comme ça – crow