En Java específicamente, pero probablemente también en otros lenguajes: ¿cuándo sería útil tener dos referencias al mismo objeto?
Ejemplo:
Dog a = new Dog();
Dob b = a;
¿Hay alguna situación en la que esto sea útil? ¿Por qué sería esta una solución preferida para usar a
cuando quiera interactuar con el objeto representado por a
?
a
yb
siempre hace referencia a lo mismoDog
, entonces no tiene sentido. Si a veces podrían, entonces es bastante útil.Respuestas:
Un ejemplo es cuando desea tener el mismo objeto en dos listas separadas :
Otro uso es cuando tienes el mismo objeto jugando varios roles :
fuente
Persona batman = new Persona("Batman"); Persona bruce = new Persona("Bruce Wayne"); Persona currentPersona = batman;
: donde tiene múltiples valores posibles (o una lista de valores disponibles) y una referencia al valor actualmente activo / seleccionado.currentPersona
apunta a un objeto u otro, pero nunca a ambos. En particular, podría ser fácilmente posible que nuncacurrentPersona
esté configurado , en cuyo caso ciertamente no es una referencia a dos objetos. Yo diría que, en mi ejemplo, ambos y son referencias a la misma instancia, pero tienen un significado semántico diferente dentro del programa.bruce
batman
currentPersona
¡Esa es realmente una pregunta sorprendentemente profunda! La experiencia de C ++ moderno (y los lenguajes que toman de C ++ moderno, como Rust) sugieren que muy a menudo, ¡no quieres eso! Para la mayoría de los datos, desea una referencia única o única ("propietaria"). Esta idea es también la razón principal de los sistemas de tipo lineal .
Sin embargo, incluso entonces, por lo general, desea algunas referencias "prestadas" de corta duración que se utilizan para acceder a la memoria brevemente pero que no duran una fracción significativa del tiempo que existen los datos. Lo más común cuando pasa un objeto como argumento a una función diferente (¡los parámetros también son variables!):
Un caso menos común cuando usa uno de dos objetos dependiendo de una condición, haciendo esencialmente lo mismo independientemente de cuál elija:
Volviendo a los usos más comunes, pero dejando atrás las variables locales, pasamos a los campos: por ejemplo, los widgets en los marcos de la GUI a menudo tienen widgets principales, por lo que su marco grande que contiene diez botones tendría al menos diez referencias apuntando a él (además de algunos más de su padre y quizás de oyentes de eventos, etc.). Cualquier tipo de gráfico de objetos, y algunos tipos de árboles de objetos (aquellos con referencias padre / hermano), tienen múltiples objetos que se refieren a cada uno de ellos. Y prácticamente cada conjunto de datos es en realidad un gráfico ;-)
fuente
Variables temporales: considere el siguiente pseudocódigo.
Las variables
max
ycandidate
pueden apuntar al mismo objeto, pero la asignación de variables cambia usando diferentes reglas y en diferentes momentos.fuente
Para complementar las otras respuestas, también puede atravesar una estructura de datos de manera diferente, comenzando desde el mismo lugar. Por ejemplo, si tuviera un
BinaryTree a = new BinaryTree(...); BinaryTree b = a
, podría recorrer el camino más a la izquierda del árbol cona
y su camino más a la derecha conb
, usando algo como:Ha pasado un tiempo desde que escribí Java, por lo que el código puede no ser correcto o sensato. Tómelo más como pseudocódigo.
fuente
Este método es excelente cuando tiene varios objetos que todos devuelven la llamada a otro objeto que puede usarse sin contexto.
Por ejemplo, si tiene una interfaz con pestañas, puede tener Tab1, Tab2 y Tab3. Es posible que también desee utilizar una variable común independientemente de la pestaña en la que se encuentre el usuario para simplificar su código y reducir la necesidad de averiguar sobre la marcha una y otra vez en qué pestaña se encuentra su usuario.
Luego, en cada una de las pestañas numeradas en Click, puede cambiar CurrentTab para hacer referencia a esa pestaña.
Ahora en su código puede llamar a "CurrentTab" con impunidad sin necesidad de saber en qué pestaña está realmente. También puede actualizar las propiedades de CurrentTab y fluirán automáticamente a la pestaña referenciada.
fuente
Hay muchos escenarios en los que
b
debe ser una referencia a un "a
" desconocido para ser útil. En particular:b
apunta en tiempo de compilación.Por ejemplo:
Parámetros
t
es una referencia a una variable desde un ámbito externo.Valores devueltos y otros valores condicionales
biggerThing
yz
son referencias aa
ob
. No sabemos cuál en tiempo de compilación.Lambdas y sus valores de retorno
x
es un parámetro (ejemplo 1 anterior) yt
es un valor de retorno (ejemplo 2 anterior)Colecciones
index["some name"]
es, en gran medida, un aspecto más sofisticadob
. Es un alias para un objeto que se creó y se insertó en la colección.Bucles
t
es una referencia a un elemento devuelto (ejemplo 2) por un iterador (ejemplo anterior).fuente
Es un punto crucial, pero en mi humilde opinión vale la pena entenderlo.
Todos los lenguajes OO siempre hacen copias de referencias, y nunca copian un objeto 'invisiblemente'. Sería mucho más difícil escribir programas si los lenguajes OO funcionaran de otra manera. Por ejemplo, funciones y métodos, nunca podrían actualizar un objeto. Java, y la mayoría de los lenguajes OO serían casi imposibles de usar sin una complejidad adicional significativa.
Se supone que un objeto en un programa tiene algún significado. Por ejemplo, representa algo específico en el mundo físico real. Por lo general, tiene sentido tener muchas referencias a lo mismo. Por ejemplo, la dirección de mi casa se puede dar a muchas personas y organizaciones, y esa dirección siempre se refiere a la misma ubicación física. Entonces, el primer punto es que los objetos a menudo representan algo que es específico, real o concreto; y así poder tener muchas referencias a lo mismo es extremadamente útil. De lo contrario, sería más difícil escribir programas.
Cada vez que pasa
a
como argumento / parámetro a otra función, por ejemplo, llamandofoo(Dog aDoggy);
o aplicando un método
a
, el código del programa subyacente hace una copia de la referencia, para producir una segunda referencia al mismo objeto.Además, si el código con una referencia copiada está en un hilo diferente, entonces ambos pueden usarse simultáneamente para acceder al mismo objeto.
Entonces, en la mayoría de los programas útiles, habrá múltiples referencias al mismo objeto, porque esa es la semántica de la mayoría de los lenguajes de programación OO.
Ahora, si lo pensamos bien, dado que pasar por referencia es el único mecanismo disponible en muchos lenguajes OO (C ++ admite ambos), podríamos esperar que sea el comportamiento predeterminado 'correcto' .
En mi humilde opinión, el uso de referencias es el valor predeterminado correcto , por un par de razones:
También hay un argumento de eficiencia; hacer copias de objetos completos es menos eficiente que copiar una referencia. Sin embargo, creo que se pierde el punto. Las referencias múltiples al mismo objeto tienen más sentido y son más fáciles de usar, ya que coinciden con la semántica del mundo físico real.
Entonces, en mi humilde opinión, generalmente tiene sentido tener múltiples referencias al mismo objeto. En los casos inusuales en los que eso no tiene sentido en el contexto de un algoritmo, la mayoría de los idiomas proporcionan la capacidad de hacer un 'clon' o copia profunda. Sin embargo, ese no es el valor predeterminado.
Creo que las personas que argumentan que este no debería ser el predeterminado están utilizando un lenguaje que no proporciona recolección de basura automática. Por ejemplo, anticuado C ++. El problema es que necesitan encontrar una manera de recolectar objetos 'muertos' y no reclamar objetos que aún pueden ser necesarios; tener múltiples referencias al mismo objeto lo hace difícil.
Creo que si C ++ tuvo una recolección de basura de costo suficientemente bajo, de modo que todos los objetos a los que se hace referencia son recolección de basura, entonces gran parte de la objeción desaparece. Todavía habrá algunos casos en los que la semántica de referencia no es lo que se necesita. Sin embargo, en mi experiencia, las personas que pueden identificar esas situaciones también suelen ser capaces de elegir la semántica adecuada de todos modos.
Creo que hay alguna evidencia de que una gran cantidad de código en un programa C ++ está ahí para manejar o mitigar la recolección de basura. Sin embargo, escribir y mantener ese tipo de código 'infraestructural' agrega costos; está ahí para hacer que el lenguaje sea más fácil de usar o más robusto. Entonces, por ejemplo, el lenguaje Go está diseñado con un enfoque en remediar algunas de las debilidades de C ++, y no tiene otra opción que la recolección de basura.
Por supuesto, esto es irrelevante en el contexto de Java. También fue diseñado para ser fácil de usar, y también lo ha sido la recolección de basura. Por lo tanto, tener múltiples referencias es la semántica predeterminada, y es relativamente seguro en el sentido de que los objetos no se reclaman mientras haya una referencia a ellos. Por supuesto, una estructura de datos podría retenerlos porque el programa no se ordena correctamente cuando realmente ha terminado con un objeto.
Entonces, volviendo a su pregunta (con un poco de generalización), ¿cuándo querría más de una referencia al mismo objeto? Prácticamente en cada situación que se me ocurre. Son la semántica predeterminada del mecanismo de paso de parámetros de la mayoría de los idiomas. Sugiero que se debe a que la semántica predeterminada de manejar objetos que existe en el mundo real tiene que ser básicamente por referencia (porque los objetos reales están ahí fuera).
Cualquier otra semántica sería más difícil de manejar.
Sugiero que el "rover" en
dl
debería ser el afectadosetOwner
o los programas se volverán difíciles de escribir, comprender, depurar o modificar. Creo que la mayoría de los programadores estarían perplejos o consternados de lo contrario.luego se vende el perro:
Este tipo de procesamiento es común y normal. Por lo tanto, la semántica de referencia es la predeterminada porque generalmente tiene más sentido.
Resumen: siempre tiene sentido tener múltiples referencias al mismo objeto.
fuente
Por supuesto, otro escenario en el que podrías terminar con:
es cuando mantiene el código y
b
solía ser un perro diferente, o una clase diferente, pero ahora es atendido pora
.En general, en el mediano plazo, debe volver a trabajar todo el código para referirse
a
directamente, pero eso puede no suceder de inmediato.fuente
Desearía esto cada vez que su programa tenga la posibilidad de extraer una entidad en la memoria en más de un lugar, posiblemente porque diferentes componentes lo están utilizando.
Identity Maps proporcionó una tienda local definitiva de entidades para que podamos evitar tener dos o más representaciones separadas. Cuando representamos el mismo objeto dos veces, nuestro cliente corre el riesgo de causar un problema de concurrencia si una referencia del objeto persiste sus cambios de estado antes que la otra instancia. La idea es que queremos asegurarnos de que nuestro cliente siempre esté tratando la referencia definitiva a nuestra entidad / objeto.
fuente
Usé esto cuando escribía un solucionador de Sudoku. Cuando sé el número de una celda al procesar filas, quiero que la columna que contiene sepa también el número de esa celda cuando procesa columnas. Entonces, tanto las columnas como las filas son matrices de objetos Cell que se superponen. Exactamente como mostró la respuesta aceptada.
fuente
En las aplicaciones web, Object Relational Mappers puede usar la carga diferida para que todas las referencias al mismo objeto de la base de datos (al menos dentro del mismo hilo) apunten a lo mismo.
Por ejemplo, si tuvieras dos tablas:
Perros:
Propietarios:
Hay varios enfoques que su ORM puede hacer si se realizan las siguientes llamadas:
En la estrategia ingenua, todas las llamadas a los modelos y las referencias relacionadas recuperan objetos separados.
Dog.find()
,Owner.find()
,owner.dogs
, Y eldog.owner
resultado en una base de datos alcanzó la primera vez, después de lo cual se guardan en la memoria. Y entonces:Sin una referencia, tiene más recuperaciones, usa más memoria e introduce la posibilidad de sobrescribir actualizaciones anteriores.
Suponga que su ORM sabe que todas las referencias a la fila 1 de la tabla perros deben apuntar a lo mismo. Luego:
En otras palabras, el uso de referencias ayuda a codificar el principio de que un solo punto de verdad (al menos dentro de ese hilo) es la fila en la base de datos.
fuente