Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

166
Vistas
¿Por qué los campos de instancia no necesitan ser finales o efectivamente finales para usarse en expresiones lambda?

Estoy practicando expresiones lambda en Java. Sé que las variables locales deben ser definitivas o efectivamente definitivas de acuerdo con la documentación de Oracle para Java SE 16 Lambda Body :

Cualquier variable local, parámetro formal o parámetro de excepción utilizado pero no declarado en una expresión lambda debe ser final o efectivamente final (§4.12.4), como se especifica en §6.5.6.1.

Aunque no dice por qué. Buscando encontré esta pregunta similar ¿ Por qué las variables en lambdas tienen que ser finales o efectivamente finales? , donde el usuario de StackOverflow "snr" respondió con la siguiente cita:

Las variables locales en Java hasta ahora han sido inmunes a las condiciones de carrera y los problemas de visibilidad porque solo son accesibles para el subproceso que ejecuta el método en el que se declaran. Pero una lambda se puede pasar del subproceso que la creó a un subproceso diferente y, por lo tanto, esa inmunidad se perdería si la lambda, evaluada por el segundo subproceso, tuviera la capacidad de mutar las variables locales.

  • Fuente: ¿Por qué la restricción en la captura de variables locales?

Esto es lo que entiendo: un método solo puede ser ejecutado por un hilo (digamos hilo_1) a la vez. Esto asegura que las variables locales de ese método en particular sean modificadas solo por thread_1. Por otro lado, una lambda se puede pasar a un hilo diferente (hilo_2), por lo que... si el hilo_1 termina con la expresión lambda y sigue ejecutando el resto del método, podría cambiar los valores de las variables locales y, en al mismo tiempo, thread_2 podría estar cambiando las mismas variables dentro de la expresión lambda. Entonces, es por eso que existe esta restricción (las variables locales deben ser definitivas o efectivamente definitivas).

Perdón por la larga explicación. ¿Estoy entendiendo esto bien?

Pero las siguientes preguntas serían:

  • ¿Por qué este caso no es aplicable a las variables de instancia?
  • ¿Qué podría pasar si thread_1 cambia las variables de instancia al mismo tiempo que thread_2 (incluso si no están ejecutando una expresión lambda)?
  • ¿Las variables de instancia están protegidas de otra manera?

No tengo mucha experiencia con Java. Lo siento si mis preguntas tienen respuestas obvias.

over 4 years ago · Santiago Trujillo
3 Respuestas
Responde la pregunta

0

Las variables de instancia se almacenan en el espacio del montón, mientras que las variables locales se almacenan en el espacio de la pila. Cada subproceso mantiene su propia pila y, por lo tanto, las variables locales no se comparten entre los subprocesos. Por otro lado, el espacio de almacenamiento dinámico es compartido por todos los subprocesos y, por lo tanto, varios subprocesos pueden modificar una variable de instancia. Existen varios mecanismos para hacer que los datos sean seguros para subprocesos y puede encontrar muchas discusiones relacionadas en esta plataforma. Solo en aras de la exhaustividad, he citado a continuación un extracto de http://web.mit.edu/6.005/www/fa14/classes/18-thread-safety/

Básicamente, existen cuatro formas de hacer que el acceso a las variables sea seguro en la concurrencia de memoria compartida:

  • Confinamiento . No comparta la variable entre hilos. Esta idea se llama confinamiento y la exploraremos hoy.
  • Inmutabilidad . Haga que los datos compartidos sean inmutables. Ya hemos hablado mucho sobre la inmutabilidad, pero existen algunas restricciones adicionales para la programación concurrente de las que hablaremos en esta lectura.
  • Tipo de datos seguro para subprocesos . Encapsule los datos compartidos en un tipo de datos seguro para subprocesos existente que haga la coordinación por usted. Hablaremos de eso hoy.
  • Sincronización . Utilice la sincronización para evitar que los subprocesos accedan a la variable al mismo tiempo. La sincronización es lo que necesita para crear su propio tipo de datos seguro para subprocesos.
over 4 years ago · Santiago Trujillo Denunciar

0

El problema no tiene nada que ver con la seguridad de los subprocesos, de verdad. Hay una respuesta simple y directa a por qué las variables de instancia siempre se pueden capturar: this siempre es efectivamente final. Es decir, siempre hay un objeto fijo conocido en el momento de la creación de una lambda que accede a una variable de instancia. Recuerde que una variable de instancia llamada foo siempre es efectivamente equivalente a this.foo .

Entonces

 class MyClass { private int foo; public void doThingWithLambda() { doThing(() -> { System.out.println(foo); }) } }

puede tener el lambda reescrito como doThing(() -> System.out.println(this.foo); }) y por lo tanto es equivalente a

 class MyClass { private int foo; public void doThingWithLambda() { final MyClass me = this; doThing(() -> { System.out.println(me.foo); }) } }

... excepto que this ya es definitivo y no necesita copiarse en otra variable local (aunque la lambda capturará la referencia).

Por supuesto, se aplican todas las advertencias normales de seguridad de subprocesos. Si sus lambdas se pasan a múltiples subprocesos y modifican variables, entonces ocurrirían exactamente las mismas cosas si no se usaran lambdas, y no se aplica seguridad de subprocesos adicional más allá de la seguridad de subprocesos de sus variables (por ejemplo, si son volátiles) o si su las lambdas utilizan otros mecanismos para acceder de forma segura a las variables. Las lambdas no hacen nada especial con respecto a la seguridad de subprocesos, y tampoco hacen nada especial con las variables de instancia; simplemente capturan una referencia a this en lugar de a la variable de instancia.

over 4 years ago · Santiago Trujillo Denunciar

0

Las otras respuestas ya brindan un gran contexto sobre por qué esto es una limitación en Java. Me gustaría ofrecer algunos antecedentes sobre cómo otros idiomas manejan esto cuando no imponen el requisito de que las variables locales se consideren inmutables (es decir final ).

El punto principal sugerido es que los valores de "montón" (es decir, campos) son intrínsecamente accesibles desde otros subprocesos, mientras que los valores de "pila" (es decir, variables locales) son intrínsecamente accesibles solo desde dentro del método que declaró los valores. Esto es verdad. Entonces, dado que los campos se almacenan en el montón, se pueden mutar después de que se haya completado el método. Por el contrario, los valores de la pila desaparecen tan pronto como finaliza el método.

Java elige respetar esta semántica, por lo que nunca se debe modificar una variable local una vez que se completa el método. Esta es una decisión de diseño justa. Sin embargo , algunos lenguajes eligen permitir la mutación a variables locales después de que el método finaliza. Entonces, ¿cómo puede ser eso?

En C# (el lenguaje con el que estoy más familiarizado, pero otros lenguajes como JavaScript también permiten estas construcciones) cuando hace referencia a una variable local dentro de una lambda, el compilador detecta eso y detrás de escena genera una clase completamente nueva para almacenar la variable local. Entonces, en lugar de declarar la variable en la pila, el compilador detecta que se ha hecho referencia a ella dentro de una lambda y, en su lugar, crea una instancia de esa clase para almacenar el valor. Entonces, este comportamiento (detrás de escena) convierte el valor de la pila en un valor del montón. (en realidad, puede descompilar dicho código y ver estas clases generadas por el compilador)

Esta decisión no es gratuita. Obviamente, es más costoso instanciar una clase solo para albergar, por ejemplo, un número entero. En Java, tiene la garantía de que esto nunca sucederá. En un lenguaje como C#, se requiere un razonamiento cuidadoso para saber si su variable ha sido "elevada" a esa clase generada.

Entonces, en última instancia, la razón se convierte en una decisión de diseño. En Java no puedes pegarte un tiro en el pie. En C# decidieron que la mayoría de las veces las consecuencias en el rendimiento no son tan importantes.

Dicho esto, la decisión de C# a menudo ha sido una fuente de confusión y errores, particularmente en torno a la variable iteradora de bucle en un bucle for (la variable de bucle i puede (y debe) ser mutada) y pasada a una lambda, como se describe en el blog de Eric Lippert. publicar Fue tan problemático que decidieron introducir un (raro) cambio importante en el compilador para la variante foreach .

Por otro lado, disfruté de la libertad de mutar variables locales dentro de un lamda en C#. Pero ninguna decisión viene sin costo.

Esta respuesta definitivamente no está tratando de defender ninguna de las decisiones, pero pensé que valía la pena elaborar algunas de estas opciones de diseño.

over 4 years ago · Santiago Trujillo Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda