El acceso a variables dentro de lambdas puede ser algo confuso, pero es sencillo si lo ves con un ejemplo.
Observa este código en el cual accedemos a los parámetros “text” y “count” desde la expresión lambda.
public static void showText(String text, int count) {
Runnable r = () -> {
for (int i = 0; i < count; i++) {
System.out.println(text);
}
};
new Thread(r).start();
}
Cuando llamamos al método showText este ejecutará un nuevo hilo con la expresión lambda y retornará de inmediato. ¿Cómo es que estas variables no se pierden dentro de la expresión lambda? Decimos que la expresión lambda ha capturado estos valores de las variables.
Estas variables son llamadas variables libres porque no son parámetros de la expresión lambda en sí misma y no están tampoco definidas dentro del código lambda.
Una expresión lambda tiene tres ingredientes:
- El bloque de código
- Parámetros
- Valores de variables libres.
Restricciones sobre variables en Lambdas
El valor de estas variables libres puede ser capturado por la lambda expresión pero tienen la restricción de que estás variables NO pueden ser modificadas dentro de la expresión lambda.
En nuestro ejemplo no podríamos modificar el valor de la variable “count”. Piensa que si diferentes trabajos ejecutan este código de forma concurrente estarían todos modificando esta contador “count”. Debemos asegurar la expresión lambda siempre “threadsafe”.
public static void showText_notWork(String text, int count) {
Runnable r = () -> {
while (count > 0) {
System.out.println(text + " " + count);
count--; // Error. Can't change !!
}
};
new Thread(r).start();
}
Dijimos que NO podemos modificar una variable compartida pero observa que si podemos modificar el valor dentro de un objeto compartido. No tendremos problemas de compilación pero nuestro código no será threadsafe por lo que el resultado puede ser impredecible si múltiples ejecuciones ocurren de forma concurrente.
En este ejemplo la asignación de la variable “data” nunca la estamos cambiando pero si estamos modificando su contenido.
public static void repeatMessage_noThreadSafe() {
List<String> data = new ArrayList<>();
data.add("file1");
// more into data...
for (String p : data) {
new Thread(() -> {
if (p.startsWith("file9")) {
data.add("xfile9"); // changing shared object
}
}).start();
}
}
Conclusión:
Vimos una breve explicación sobre el scope de las variables en expresiones lambda y porqué no es posible modificarlas.
Código de ejemplo: