La modification d'une variable locale dans forEach
donne une erreur de compilation:
Ordinaire
int ordinal = 0;
for (Example s : list) {
s.setOrdinal(ordinal);
ordinal++;
}
Avec Lambda
int ordinal = 0;
list.forEach(s -> {
s.setOrdinal(ordinal);
ordinal++;
});
aucune idée pour résoudre ça?
Réponses:
Utilisez un emballage
Tout type d'emballage est bon.
Avec Java 8+ , utilisez un
AtomicInteger
:... ou un tableau:
Avec Java 10+ :
Remarque: soyez très prudent si vous utilisez un flux parallèle. Vous pourriez ne pas vous retrouver avec le résultat attendu. D'autres solutions comme celle de Stuart pourraient être plus adaptées à ces cas.
Pour les types autres que
int
Bien sûr, cela est toujours valable pour les types autres que
int
. Il vous suffit de changer le type d'habillage en unAtomicReference
ou un tableau de ce type. Par exemple, si vous utilisez aString
, procédez comme suit:Utilisez un tableau:
Ou avec Java 10+:
la source
int[] ordinal = { 0 };
? Pouvez-vous l'expliquer. Merciclass MyClass$1 { int value; }
Dans une classe habituelle, cela signifie que vous créez une classe avec une variable privée de package nomméevalue
. C'est la même chose, mais la classe est en fait anonyme pour nous, pas pour le compilateur ou la JVM. Java compilera le code comme s'il l'étaitMyClass$1 wrapper = new MyClass$1();
. Et la classe anonyme devient juste une autre classe. En fin de compte, nous venons d'ajouter du sucre syntaxique par-dessus pour le rendre lisible. En outre, la classe est une classe interne avec un champ package-private. Ce champ est utilisable.C'est assez proche d'un problème XY . Autrement dit, la question posée est essentiellement de savoir comment muter une variable locale capturée à partir d'un lambda. Mais la tâche actuelle consiste à numéroter les éléments d'une liste.
D'après mon expérience, plus de 80% du temps, il y a une question de savoir comment muter un local capturé à partir d'un lambda, il y a une meilleure façon de procéder. Cela implique généralement une réduction, mais dans ce cas, la technique d'exécution d'un flux sur les index de liste s'applique bien:
la source
list
est uneRandomAccess
listeSi vous avez seulement besoin de passer la valeur de l'extérieur dans le lambda, et de ne pas la sortir, vous pouvez le faire avec une classe anonyme régulière au lieu d'un lambda:
la source
Comme les variables utilisées de l'extérieur du lamda doivent être (implicitement) finales, vous devez utiliser quelque chose comme
AtomicInteger
ou écrire votre propre structure de données.Voir https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html#accessing-local-variables .
la source
Si vous êtes sur Java 10, vous pouvez utiliser
var
pour cela:la source
Une alternative à
AtomicInteger
(ou à tout autre objet capable de stocker une valeur) est d'utiliser un tableau:Mais voyez la réponse de Stuart : il y a peut-être une meilleure façon de traiter votre cas.
la source
Je sais que c'est une vieille question, mais si vous êtes à l'aise avec une solution de contournement, compte tenu du fait que la variable externe doit être finale, vous pouvez simplement le faire:
Peut-être pas le plus élégant, ni même le plus correct, mais ça fera l'affaire.
la source
Vous pouvez le résumer pour contourner le compilateur, mais n'oubliez pas que les effets secondaires dans les lambdas sont déconseillés.
Pour citer le javadoc
la source
J'ai eu un problème légèrement différent. Au lieu d'incrémenter une variable locale dans forEach, j'avais besoin d'assigner un objet à la variable locale.
J'ai résolu ce problème en définissant une classe de domaine interne privé qui englobe à la fois la liste sur laquelle je veux parcourir (countryList) et la sortie que j'espère obtenir de cette liste (foundCountry). Ensuite, en utilisant Java 8 "forEach", j'itère sur le champ de liste, et lorsque l'objet que je veux est trouvé, j'attribue cet objet au champ de sortie. Ainsi, cela affecte une valeur à un champ de la variable locale, sans modifier la variable locale elle-même. Je crois que puisque la variable locale elle-même n'est pas modifiée, le compilateur ne se plaint pas. Je peux ensuite utiliser la valeur que j'ai capturée dans le champ de sortie, en dehors de la liste.
Objet de domaine:
Objet wrapper:
Opération itérative:
Vous pouvez supprimer la méthode de classe wrapper "setCountryList ()" et rendre le champ "countryList" final, mais je n'ai pas eu d'erreurs de compilation en laissant ces détails tels quels.
la source
Pour avoir une solution plus générale, vous pouvez écrire une classe Wrapper générique:
(ceci est une variante de la solution donnée par Almir Campos).
Dans le cas précis ce n'est pas une bonne solution, comme
Integer
c'est pire queint
pour votre propos, de toute façon cette solution est plus générale je pense.la source
Oui, vous pouvez modifier les variables locales à partir de lambdas (de la manière indiquée par les autres réponses), mais vous ne devriez pas le faire. Les lambdas ont été conçus pour un style de programmation fonctionnel et cela signifie: Aucun effet secondaire. Ce que vous voulez faire est considéré comme un mauvais style. C'est également dangereux en cas de flux parallèles.
Vous devez soit trouver une solution sans effets secondaires, soit utiliser une boucle for traditionnelle.
la source