react objetos mutables mutabilidad inmutables inmutable inmutabilidad ejemplos dios derecho java immutability

java - objetos - mutable e inmutable



¿Datos empíricos sobre los efectos de la inmutabilidad? (9)

Hoy en clase, mi profesor estaba discutiendo cómo estructurar una clase. El curso utiliza principalmente Java y tengo más experiencia en Java que el profesor (proviene de un fondo en C ++), por lo que mencioné que en Java uno debería favorecer la inmutabilidad. Mi profesor me pidió que justificara mi respuesta y di las razones que escuché de la comunidad de Java:

  1. Seguridad (especialmente con roscado)
  2. Recuento de objetos reducido
  3. Permite ciertas optimizaciones (especialmente para recolectores de basura)

El profesor desafió mi afirmación diciendo que le gustaría ver una medición estadística de estos beneficios. Cité una gran cantidad de evidencia anecdótica, pero incluso mientras lo hacía, me di cuenta de que tenía razón: por lo que sé, no ha habido un estudio empírico de si la inmutabilidad en realidad proporciona los beneficios que promete en el código del mundo real. Sé que lo hace por experiencia, pero las experiencias de otros pueden diferir.

Entonces, mi pregunta es, ¿se han realizado estudios estadísticos sobre los efectos de la inmutabilidad en el código del mundo real?


Entonces, mi pregunta es, ¿se han realizado estudios estadísticos sobre los efectos de la inmutabilidad en el código del mundo real?

Yo diría que su profesor solo está siendo obtuso, no necesariamente intencionalmente o incluso algo malo. Es solo que la pregunta es demasiado vaga. Dos problemas reales con la pregunta:

  • "Los estudios estadísticos sobre el efecto de [x]" realmente no significan nada si no especifica qué tipo de medidas está buscando.
  • El "código del mundo real" no significa nada a menos que indique un dominio específico. El código del mundo real incluye informática científica, desarrollo de juegos, motores de blogs, generadores de pruebas automatizados, procedimientos almacenados, kernals de sistemas operativos, etc.

Para su valor, la capacidad del compilador para optimizar objetos inmutables está bien documentada. La parte superior de mi cabeza:

  • El compilador de Haskell realiza la deforestation (también llamada fusión de atajo), donde Haskell transformará el map f . map g expresión map f . map g map f . map g al map f . g map f . g . Dado que las funciones de Haskell son inmutables, se garantiza que estas expresiones producirán una salida equivalente, pero la segunda función se ejecuta dos veces más rápido ya que no es necesario crear una lista intermedia.
  • Eliminación de subexpresiones comunes donde podríamos convertir x = foo(12); y = foo(12) x = foo(12); y = foo(12) a temp = foo(12); x = temp; y = temp; temp = foo(12); x = temp; y = temp; solo es posible si el compilador puede garantizar que foo es una función pura. Que yo sepa, el compilador de D puede realizar sustituciones como esta utilizando palabras clave pure e immutable . Si recuerdo correctamente, algunos compiladores de C y C ++ optimizarán agresivamente las llamadas a estas funciones marcadas como "puras" (o cualquiera que sea la palabra clave equivalente).
  • Mientras no tengamos un estado mutable, un compilador suficientemente inteligente puede ejecutar bloques lineales de subprocesos múltiples de código con la garantía de que no corromperemos el estado de las variables en otro subproceso.

Con respecto a la concurrencia, los inconvenientes de la concurrencia con el estado mutable están bien documentados y no necesitan ser reexpresados.

Claro, esto es toda evidencia anecdótica, pero eso es lo mejor que obtendrás. El debate inmutable frente a mutable es en gran medida una coincidencia, y no va a encontrar un documento que haga una generalización radical como "la programación funcional es superior a la programación imperativa".

A lo sumo , probablemente encontrará que puede resumir los beneficios de inmutable frente a mutable en un conjunto de mejores prácticas en lugar de estudios codificados y estadísticas. Por ejemplo, el estado mutable es el enemigo de la programación multiproceso; por otro lado, las colas y matrices mutables suelen ser más fáciles de escribir y más eficientes en la práctica que sus variantes inmutables.

Se necesita práctica, pero con el tiempo aprendes a usar la herramienta adecuada para el trabajo, en lugar de adivinar tu paradigma de mascota favorito en el proyecto.


¿Qué medirías objetivamente? GC y el recuento de objetos podría medirse con versiones mutables / inmutables del mismo programa (aunque lo típico que sería sería subjetivo, entonces este es un argumento bastante débil). No puedo imaginar cómo se podría medir la eliminación de errores de subprocesamiento, excepto de forma anecdótica en comparación con un ejemplo del mundo real de una aplicación de producción plagada de problemas intermitentes solucionados al agregar inmutabilidad.


Como han afirmado otros comentarios, sería muy, muy difícil recopilar estadísticas sobre los méritos de los objetos inmutables, ya que sería prácticamente imposible encontrar casos de control - pares de aplicaciones de software que sean iguales en todos los aspectos, excepto que uno utiliza inmutable Los objetos y el otro no. (En casi todos los casos, afirmaría que una versión de ese software se escribió poco después de la otra, y aprendí muchas lecciones de la primera, por lo que las mejoras en el rendimiento tendrán muchas causas). Cualquier programador experimentado que piense en esto para Un momento debe darse cuenta de esto. Creo que tu profesor está intentando desviar tu sugerencia.

Mientras tanto, es muy fácil hacer argumentos convincentes a favor de la inmutabilidad, al menos en Java, y probablemente en C # y otros lenguajes OO. Como afirma Yishai, Eficaz Java hace que este argumento sea bueno. Lo mismo ocurre con la copia de Java Concurrency in Practice que se encuentra en mi estante.


Creo que tu profesor está siendo muy terco (probablemente deliberadamente, para empujarte a una comprensión más completa). Realmente, los beneficios de la inmutabilidad no son tanto lo que el complaciente puede hacer con las optimizaciones, sino que realmente es mucho más fácil para los humanos leer y entender. Una variable que se garantiza que se establecerá cuando se crea el objeto y que se garantiza que no se modificará más adelante, es mucho más fácil de asimilar y razonar que una que tenga este valor ahora, pero podría configurarse en otro valor más adelante .

Esto es especialmente cierto con el subprocesamiento, ya que no necesita preocuparse por los cachés y monitores del procesador y todo lo que viene acompañado de evitar modificaciones concurrentes, cuando el lenguaje garantiza que no es posible que ocurra tal modificación.

Y una vez que expresa los beneficios de la inmutabilidad, ya que "el código es más fácil de seguir", se siente un poco más tonto pedir mediciones empíricas de los aumentos de productividad en comparación con "más fácil de seguir".

Por otro lado, el compilador y el punto de acceso probablemente pueden realizar ciertas optimizaciones basadas en saber que un valor nunca puede cambiar; como usted, tengo la sensación de que esto ocurrirá y es algo bueno, pero no estoy seguro de los detalles. Es mucho más probable que haya datos empíricos para los tipos de optimización que pueden ocurrir, y cuánto más rápido es el código resultante.


La inmutabilidad es algo bueno para los objetos de valor. Pero ¿qué hay de otras cosas? Imagina un objeto que crea una estadística:

Stats s = new Stats (); ... some loop ... s.count (); s.end (); s.print ();

que debe imprimir "Procesado 536.21 filas / s". ¿Cómo planeas implementar count() con un inmutable? Incluso si utiliza un objeto de valor inmutable para el contador en sí, s no puede ser inmutable, ya que tendría que reemplazar el objeto del contador dentro de sí mismo. La única salida sería:

s = s.count ();

lo que significa copiar el estado de s para cada ronda en el bucle. Si bien esto se puede hacer, seguramente no es tan eficiente como incrementar el contador interno.

Además, la mayoría de las personas no utilizarían este derecho de API porque esperaban que count() modificara el estado del objeto en lugar de devolver uno nuevo. Así que en este caso, crearía más errores.


La mayor ventaja de la inmutabilidad en Java, en mi opinión, es la simplicidad. Se vuelve mucho más sencillo razonar sobre el estado de un objeto, si ese estado no puede cambiar. Por supuesto, esto es aún más importante en un entorno de múltiples subprocesos, pero incluso en programas simples y lineales de un solo subproceso puede hacer las cosas mucho más fáciles de entender.

Vea esta página para más ejemplos.


Los objetos inmutables permiten el código que comparte el valor de un objeto al compartir una referencia. Los objetos mutables, sin embargo, tienen la identidad del código que quiere compartir la identidad de un objeto para hacerlo compartiendo una referencia. Ambos tipos de intercambio son esenciales en la mayoría de las aplicaciones. Si uno no tiene objetos inmutables disponibles, es posible compartir valores copiándolos en objetos nuevos u objetos suministrados por el destinatario deseado de esos valores. Conseguir mi sin objetos mutables es mucho más difícil. Uno podría de alguna manera "falsificar" objetos mutables diciendo stateOfUniverse = stateOfUniverse.withSomeChange(...) , pero requeriría que nada más modifique stateOfUniverse mientras su método withSomeChange se esté ejecutando [excluyendo cualquier tipo de subprocesos múltiples]. Además, si uno, por ejemplo, intentara rastrear una flota de camiones, y parte del código estuviera interesado en un camión en particular, sería necesario que ese código busque siempre ese camión en una tabla de camiones cada vez que pueda haber cambiado. .

Un mejor enfoque es subdividir el universo en entidades y valores. Las entidades tendrían características cambiantes, pero una identidad inmutable, por lo que una ubicación de almacenamiento de, por ejemplo, un Truck podría seguir identificando el mismo camión incluso cuando el camión cambia de posición, carga y descarga la carga, etc. Los valores generalmente no tendrían una identidad particular , pero tendría características inmutables. Un Truck puede almacenar su ubicación como tipo WorldCoordinate . Un WorldCoordinate que represente 45.6789012N 98.7654321W continuará hasta que exista alguna referencia al mismo; si un camión que estaba en esa ubicación se moviera ligeramente hacia el norte, crearía una nueva WorldCoordinate que representaría 45.6789013N 98.7654321W, abandonaría la antigua y almacenaría una referencia a la nueva.

En general, es más fácil razonar sobre el código cuando todo encapsula un valor inmutable o una identidad inmutable, y cuando las cosas que se supone que tienen una identidad inmutable son mutables. Si uno no quisiera usar objetos mutables fuera de una variable stateOfUniverse , actualizar la posición de un camión requeriría algo como:

ImmutableMapping<int,Truck> trucks = stateOfUniverse.getTrucks(); Truck myTruck = trucks.get(myTruckId); myTruck = myTruck.withLocation(newLocation); trucks = trucks.withItem(myTruckId,myTruck); stateOfUniverse = stateOfUniverse.withTrucks(trucks);

pero razonar sobre ese código sería más difícil de lo que sería:

myTruck.setLocation(newLocation);


Me gustaría señalar el punto 15 en Java efectiva. El valor de la inmutabilidad está en el diseño (y no siempre es apropiado, es solo una buena primera aproximación) y las preferencias de diseño rara vez se argumentan desde un punto de vista estadístico, pero hemos visto objetos mutables (Calendario, Fecha) que han ido muy mal, y los reemplazos serios (JodaTime, JSR-310) han optado por la inmutabilidad.


  • No discutas con el prof. No tienes nada que ganar.
  • Estas son preguntas abiertas, como la escritura dinámica frente a la estática. A veces pensamos que las técnicas funcionales que involucran datos inmutables son mejores por varias razones, pero hasta ahora es principalmente una cuestión de estilo.