Identidad de objeto y mutabilidad


8

Estaba leyendo una propuesta para los tipos de valor en Java , y me encontré con esta oración: "La identidad del objeto sirve solo para soportar la mutabilidad, donde el estado de un objeto puede ser mutado pero sigue siendo el mismo objeto intrínseco".

Por lo que entiendo (aunque provisionalmente), la identidad del objeto es la idea de que su variable actúe como un puntero o referencia a un objeto ubicado en otra parte de la memoria (como los objetos instanciados en el montón en Java o C #). Entonces, ¿qué tendría esto que ver con la mutabilidad del objeto? ¿Esto implica que, por ejemplo, los objetos instanciados en la pila en C ++ son inmutables? Tengo problemas para ver el enlace aquí.


"¿Esto implica que, por ejemplo, los objetos instanciados en la pila en C ++ son inmutables" Creo que lo que hace que un objeto sea inmutable es cómo está diseñado y qué permite, no dónde está instanciado o su identidad. ¿Qué piensas?
Jordania

¿Hay un foro de discusión asociado con esa propuesta? Sugeriría que un tipo de valor se considere como un conjunto de variables unidas con cinta adhesiva. Dado struct Point {public int x,y;}, una declaración Point ptdebería declarar efectivamente dos variables llamadas pt.xy pt.y. Un parámetro de tipo de método Pointdebe ser dos intparámetros. Bajo tales reglas, los únicos aspectos que requerirían cualquier cambio en el tiempo de ejecución serían matrices y valores de retorno de funciones. Las matrices que contienen solo primitivas o solo objetos podrían manejarse ...
supercat

... mediante el uso de matrices primitivas o matrices de objetos y elementos intercalados. Las matrices que contienen una mezcla de primitivos y valores podrían acomodarse como una matriz de objetos, cuyo primer elemento era una referencia a una matriz de valores. Si el número de objetos o primitivas en una estructura fuera limitado, los retornos de funciones podrían manejarse sin cambios en el tiempo de ejecución, agregando miembros Threadpara mantenerlos.
supercat

1
@supercat la forma en que funciona el JCP, si alguien lo quiere en el lenguaje central y grita lo suficientemente fuerte que "Java está muerto si no consigue esto" se incluye.
Jwenting

Puede encontrar útil este artículo, sobre este mismo tema: Los objetos deben ser inmutables
yegor256

Respuestas:


10

Antes de abordar la identidad, definamos lo que entendemos por igualdad un poco más precisamente. Decimos que dos cosas son iguales si y solo si no podemos distinguirlas (ver: Identidad de indiscernibles ). Eso significa que si dos cosas son iguales o no depende de los medios que tengamos para inspeccionarlas.

Pensemos en eso un poco más en términos de programación. Dejemos nuestras ideas preconcebidas en la puerta y supongamos que estamos trabajando en un nuevo lenguaje desconocido donde todas las variables y valores son inmutables. Según la definición anterior, dos valores Ay Bson iguales si y solo si NO hay programas en el lenguaje que produzcan resultados diferentes cuando Ase usan en lugar de Bo viceversa. Digamos Ay Bson flotantes (IEEE 754), y cuando se sustituyen en la expresión _ + 1.0, el resultado es 1.0para ambos Ay B. Seguramente Ay Bambos son cero. ¿Son iguales? Eso depende: ¿el lenguaje proporciona alguna función que me permita determinar el signo del cero?? Si no es así, son iguales; si lo hace, pueden no serlo.

Por lo tanto, dos valores son iguales cada vez que dan los mismos resultados para todas las combinaciones posibles de operaciones que admiten. Los valores inmutables en particular no producen resultados diferentes dependiendo de las operaciones que se les aplicaron previamente. Por esa razón, no nos importa si dos variables apuntan a dos copias del mismo valor o si ambas apuntan a la misma copia.

¿Qué tiene esto que ver con la mutabilidad? La mutabilidad implica que nuestro lenguaje tiene una noción de una celda de memoria cuyos contenidos pueden sobrescribirse. Digamos que agregamos soporte para celdas de memoria mutables a nuestro idioma:

  • ref <value>crea una nueva celda de memoria, distinta de todas las demás, inicializada en <value>.
  • <variable> := <value> sobrescribe el contenido de una celda de referencia.
  • !<variable> devuelve el valor actualmente almacenado en una celda de referencia.

Ahora pensemos en lo que significa la igualdad para las celdas de memoria. Supongamos A = ref 0y B = A. Considere este programa:

A := 1
print(!_)

Sustituyendo las Aimpresiones en blanco 1y sustituyendo también por las Bimpresiones 1. Ahora supongamos A = ref 0y B = ref 0. En este caso, sustituyendo en el programa imprime anteriores 1y 0, desde ahora Ay Bpunto a distintas celdas de memoria.

Por lo tanto, nos importa si dos referencias apuntan a la misma celda de memoria o diferentes celdas de memoria. Como eso es importante, sería útil tener una forma eficiente y general de distinguir dos referencias. Nuestro método actual de comparar los valores que tienen, y si son iguales, mutar uno de ellos es problemático por varias razones:

  • Depende de poder comparar los valores almacenados en las celdas de memoria para la igualdad. La igualdad no tiene sentido para todos los tipos; por ejemplo, generalmente no tiene sentido para las funciones, porque no hay un método general para determinar si dos funciones desconocidas son iguales (esto es aventurarse en el territorio del Problema de detención). Entonces, dadas dos referencias a las celdas de memoria que almacenan funciones, no podemos comparar las funciones que tienen para la igualdad.
  • Depende de tener algún valor que podamos asignar a una de las dos referencias. Entonces, incluso si la igualdad tiene sentido para todos los tipos en el idioma, todavía necesitamos acceso a un valor para cada tipo que queremos comparar. ¿Qué pasa si construir un valor de ese tipo tiene efectos secundarios?
  • El valor de referencia que usamos para mutar una de las referencias debe ser diferente del valor que la celda de memoria ya tiene, por lo que en realidad necesitamos dos valores.
  • El código para comparar referencias de diferentes tipos se verá exactamente igual, salvo para los dos valores que usamos.
  • Necesitamos respaldar y restaurar el valor de la referencia que mutamos para evitar cambiar el significado del programa.

Por lo tanto, sería útil que el lenguaje proporcione una operación para verificar directamente si dos referencias apuntan a la misma celda de memoria mutable. Tal función no tiene sentido para valores inmutables; de hecho, diría que es francamente dañino. Si existía una forma de saber si dos 1s están almacenados en diferentes lugares en la memoria, entonces puede haber programas que se preocupen si paso uno 1u otro. Realmente no quiero preocuparme si tengo "el derecho 1"; ¡las matemáticas ya son bastante difíciles! Por lo tanto, está claro que poder verificar la igualdad de memoria es principalmente útil para los tipos mutables.


5

Imagen que tiene dos objetos, por ejemplo, a instancias de la clase List. Ambas listas tienen el mismo contenido. Ahora está leyendo las listas y calcula y genera algo basado en su contenido. ¿Importa qué lista usas? No lo hace porque tienen el mismo contenido.

Pero, ¿y si se cambia una de las listas? Ahora importa cuál elijas para leer y generar algo. Por lo tanto, debe poder distinguir entre ellos y desea, incluso en algunas situaciones, poder verificar si dos variables apuntan al mismo objeto (o la lista aquí).

Sin embargo, si no puede cambiar el contenido de una lista, entonces ni siquiera necesita tener dos objetos de lista con el mismo contenido porque no puede cambiarlos de todos modos. Si desea "cambiar" el contenido, en su lugar creará un nuevo objeto de lista con contenido diferente, que es como dije un nuevo objeto. Entonces, desde este punto de vista, la identidad no importa. Solo el contenido lo hace y solo el contenido debe usarse para comparar dos listas.

También tenga en cuenta que el compilador podría apuntar al mismo objeto de lista incluso si declara dos objetos porque solo necesita almacenar su contenido una vez, ya que no puede cambiar.


0

La identidad del objeto no existe "sólo" a la mutabilidad de apoyo, pero tiene otros usos, así [de hecho, una instancia de Objecttipo es útil sólo como un testigo de identidad, ya que cuenta con atributos no observable-mutables en absoluto!] En cambio, la identidad es una frecuencia - característica no deseada de que un objeto mutable adquirirá cada vez que existan múltiples referencias al objeto, no todos están controlados por un solo propietario y las entidades ajenas al propietario pueden, sin el conocimiento del propietario, realizar o ver cambios en ese objeto .

Suponga que algún campo estático Xcontiene una referencia a la instancia de elemento único de int[], y X [0] = 5. El campo estático Ytambién contiene un elemento para una instancia de un solo elemento de int[], y Y [0] = 5. Si el código cada vez llama IdentityHashCase()en Xe Y, o si los análisis de código X==Y, será capaz de discernir si Xy Yidentificar la misma instancia del int[]o diferentes instancias. Del mismo modo, si el código hace algo así X[0]+=1; Y[0]+=2;, el comportamiento dependerá de si Xy de Yidentificar a la misma instancia o instancias diferentes. Sin embargo, si el código nunca prueba Xy Yexplícitamente la igualdad de referencia, ni verifica su código hash de identidad, o hace algo más "relacionado con la referencia", y si X[0]yY[0]nunca se modifican, entonces, Xy Yserán equivalentes independientemente de si identifican las mismas matrices o matrices diferentes.

Una de las cosas desagradables sobre la identidad en Java o .NET es que la identidad de un objeto depende fundamentalmente del paradero de cada entidad en el universo que pueda hacer o ver cambios en ese objeto. Si una referencia a un objeto se expone libremente a un código externo, el propietario del objeto perderá el control sobre él y nunca podrá recuperarlo.

Los tipos de valor, a diferencia de los objetos, nunca se pueden observar o modificar, excepto por el objeto o método en el que se declaran. Por lo tanto, un objeto que contiene un campo de tipo de valor no tiene que preocuparse por perder el control sobre él, ya que es semánticamente imposible que eso suceda. En .NET (aunque no Java) es posible pasar una variable, campo, elemento de matriz u otra ubicación de almacenamiento como un "parámetro de referencia". Si lo hace, permitirá temporalmente que el método observe o modifique el contenido de la ubicación de almacenamiento aprobada mientras dure su ejecución, pero se garantiza que cualquier "byref" aprobado (el término técnico para lo que se pasa) se destruirá antes de que el método regrese, asegurando así que la persona que llama mantiene el control sobre los datos que contiene,identidad no deseada


En Java, Objecttiene un atributo mutable muy importante, la exclusión mutua asociada.
Jan Hudec

@ JanHudec: Yo diría que las cerraduras de monitor en Java y .NET se usan Objectcomo token de identidad. En la medida en que se vean como estado mutable encapsulante en un objeto, no existe un objeto inmutable. Es irónico que Java a veces se vea como un "lenguaje de enseñanza", dado que un objetivo de diseño más significativo era simplificar el tiempo de ejecución utilizando un solo tipo de referencia; desde una perspectiva semántica, hubiera sido mejor distinguir los tipos que tienen una identidad de los que no. El beneficio principal de los tipos inmutables es ...
supercat

... que las instancias inmutables que encapsulan el mismo estado se pueden usar indistintamente, pero no hay forma de que un tipo pueda garantizarlo. Si el código se usa new String("Hello");como una clave en un IdentityHashMap, la cadena se vería como cualquier otra cadena que encapsula esos caracteres a cualquier código que no conociera ese mapa, pero no sería sustituible porque su objeto subyacente tendría un token de identidad que era diferente de cualquier otro objeto en todo el universo.
supercat

No importa si el bloqueo está técnicamente contenido o almacenado en una estructura de datos externa, semánticamente se comporta como una propiedad mutable que permite distinguir la identidad del objeto. Y estoy casi seguro de que vi la descripción de bajo nivel de Java Object donde claramente incluía el bloqueo. Una de las razones de la ineficiencia extrema de la memoria de Java.
Jan Hudec

@ JanHudec: Semánticamente, ningún objeto puede ser más inmutable que Object. Cualquier definición de inmutabilidad a nivel de clase que se satisfaría, por ejemplo Integer, sería igualmente satisfecha por Object. Además, en general, cuando se dice que un objeto encapsula el estado mutable, es posible copiar significativamente ese estado de una instancia a otra. No creo que haya forma de copiar el estado de bloqueo de un objeto a otro.
supercat
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.