He estado reflexionando durante un tiempo por qué Java y C # (y estoy seguro de que otros lenguajes) predeterminan la igualdad de referencia ==
.
En la programación que hago (que ciertamente es solo un pequeño subconjunto de problemas de programación), casi siempre quiero igualdad lógica al comparar objetos en lugar de igualdad de referencia. Estaba tratando de pensar por qué ambos lenguajes tomaron esta ruta en lugar de invertirla y tener ==
igualdad lógica y usarla .ReferenceEquals()
como referencia de igualdad.
Obviamente, el uso de la igualdad de referencia es muy simple de implementar y ofrece un comportamiento muy consistente, pero no parece encajar bien con la mayoría de las prácticas de programación que veo hoy.
No deseo parecer ignorante de los problemas al tratar de implementar una comparación lógica, y que tiene que implementarse en cada clase. También me doy cuenta de que estos lenguajes fueron diseñados hace mucho tiempo, pero la pregunta general se mantiene.
¿Hay algún beneficio importante en el incumplimiento de esto que simplemente me estoy perdiendo, o parece razonable que el comportamiento predeterminado sea la igualdad lógica y, por defecto, la igualdad de referencia no existe una igualdad lógica para la clase?
fuente
Equals()
, no cambia automáticamente el comportamiento de==
?Respuestas:
C # lo hace porque Java lo hizo. Java lo hizo porque Java no admite la sobrecarga del operador. Como la igualdad de valores debe redefinirse para cada clase, no puede ser un operador, sino que debe ser un método. OMI, esta fue una mala decisión. Es mucho más fácil escribir y leer
a == b
quea.equals(b)
, y mucho más natural para los programadores con experiencia en C o C ++, peroa == b
casi siempre está mal. Los errores del uso de==
donde.equals
se requería han desperdiciado miles de horas de programador.fuente
==
de muchas clases y hace unos meses descubrí que algunos desarrolladores no sabían lo que==
realmente estaba haciendo. Siempre existe este riesgo cuando la semántica de alguna construcción no es obvia. Laequals()
notación me dice que estoy usando un método personalizado y que tengo que buscarlo en alguna parte. En pocas palabras: creo que la sobrecarga del operador es un problema abierto en general.+
por ejemplo, qué suma (de valores numéricos) y concatenación de cadenas al mismo tiempo.a == b
ser más natural para los programadores con experiencia en C, ya que C no admite la sobrecarga del operador definida por el usuario? (Por ejemplo, la forma C de comparar cadenas esstrcmp(a, b) == 0
, noa == b
.)char *
. Me parece obvio que comparar dos punteros para la igualdad no es lo mismo que una comparación de cadenas.La respuesta corta: consistencia
Sin embargo, para responder adecuadamente a su pregunta, sugiero que demos un paso atrás y veamos la cuestión de qué significa igualdad en un lenguaje de programación. Existen al menos TRES posibilidades diferentes, que se utilizan en varios idiomas:
Estos tres tipos de igualdad a menudo se usan porque son convenientes de implementar: un compilador puede generar fácilmente las tres comprobaciones de igualdad (en el caso de una igualdad profunda, el compilador podría necesitar usar bits de etiqueta para evitar bucles infinitos si una estructura ser comparado tiene referencias circulares). Pero hay otro problema: ninguno de estos podría ser apropiado.
En sistemas no triviales, la igualdad de objetos a menudo se define como algo entre la igualdad profunda y la igualdad de referencia. Para verificar si queremos considerar dos objetos como iguales en un determinado contexto, podríamos requerir que se comparen algunos atributos por su posición en la memoria y otros por una profunda igualdad, mientras que algunos atributos pueden ser algo completamente diferente. Lo que realmente nos gustaría es un "cuarto tipo de igualdad", uno realmente agradable, a menudo llamado en la literatura igualdad semántica . Las cosas son iguales si son iguales, en nuestro dominio. =)
Para que podamos volver a su pregunta:
¿Qué queremos decir cuando escribimos 'a == b' en cualquier idioma? Idealmente, siempre debería ser lo mismo: igualdad semántica. Pero eso no es posible.
Una de las principales consideraciones es que, al menos para tipos simples como números, esperamos que dos variables sean iguales después de la asignación del mismo valor. Vea abajo:
En este caso, esperamos que 'a sea igual a b' en ambas declaraciones. Cualquier otra cosa sería una locura. La mayoría (si no todos) de los idiomas siguen esta convención. Por lo tanto, con tipos simples (también conocidos como valores) sabemos cómo lograr la igualdad semántica. Con los objetos, eso puede ser algo completamente diferente. Vea abajo:
Esperamos que el primer 'si' siempre sea cierto. Pero, ¿qué esperas del segundo 'si'? Realmente depende ¿Puede 'DoSomething' cambiar la igualdad (semántica) de ayb?
El problema con la igualdad semántica es que el compilador no puede generarlo automáticamente para los objetos, ni es obvio a partir de las asignaciones . Se debe proporcionar un mecanismo para que el usuario defina la igualdad semántica. En lenguajes orientados a objetos, ese mecanismo es un método heredado: igual . Al leer un fragmento de código OO, no esperamos que un método tenga la misma implementación exacta en todas las clases. Estamos acostumbrados a la herencia y la sobrecarga.
Con los operadores, sin embargo, esperamos el mismo comportamiento. Cuando vea 'a == b', debe esperar el mismo tipo de igualdad (de los 4 anteriores) en todas las situaciones. Por lo tanto, para lograr la coherencia, los diseñadores de idiomas utilizaron la igualdad de referencia para todos los tipos. No debe depender de si un programador ha anulado un método o no.
PD: El lenguaje Dee es ligeramente diferente de Java y C #: el operador igual significa igualdad superficial para tipos simples e igualdad semántica para clases definidas por el usuario (con la responsabilidad de implementar la operación = que corresponde al usuario; no se proporciona ningún valor predeterminado). Como, para los tipos simples, la igualdad superficial siempre es igualdad semántica, el lenguaje es consistente. Sin embargo, el precio que paga es que el operador igual no está definido por defecto para los tipos definidos por el usuario. Tienes que implementarlo. Y, a veces, eso es aburrido.
fuente
When you see ‘a == b’ you should expect the same type of equality (from the 4 above) in all situations.
Los diseñadores de lenguaje de Java utilizaron la igualdad de referencia para objetos y la igualdad semántica para primitivas. No es obvio para mí que esta fue la decisión correcta, o que esta decisión es más "consistente" que permitir==
que se sobrecargue por la igualdad semántica de los objetos.a
yb
son del mismo tipo, la expresióna==b
prueba sia
y sib
tiene la misma cosa. Si uno de ellos tiene una referencia al objeto # 291, y el otro tiene una referencia al objeto # 572, no tienen la misma cosa. El contenido de los objetos # 291 y # 572 puede ser equivalente, pero las variables en sí contienen cosas diferentes.a == b
y saber lo que hace. Del mismo modo, puede vera.equals(b)
y presumir que se sobrecargaequals
. Si sea == b
llamaa.equals(b)
(si se implementa), ¿se compara por referencia o por contenido? ¿No te acuerdas? Debe verificar la clase A. El código ya no es tan rápido de leer si ni siquiera está seguro de cómo se llama. Sería como si se permitieran métodos con la misma firma, y el método que se llama depende de cuál sea el alcance actual. Tales programas serían imposibles de leer.Porque este último enfoque sería confuso. Considerar:
¿Debería imprimirse este código
"ok"
, o debería arrojar unNullPointerException
?fuente
Para Java y C #, el beneficio radica en que están orientados a objetos.
Desde el punto de vista del rendimiento , el código más fácil de escribir también debería ser más rápido: dado que OOP pretende que los elementos lógicamente distintos sean representados por diferentes objetos, la verificación de la igualdad de referencia sería más rápida, teniendo en cuenta que los objetos pueden llegar a ser bastante grandes.
Desde un punto de vista lógico , la igualdad de un objeto con respecto a otro no tiene que ser tan obvia como la comparación de las propiedades de igualdad del objeto (por ejemplo, ¿cómo se interpreta lógicamente nulo == nulo? Esto puede diferir de un caso a otro).
Creo que todo se reduce a su observación de que "siempre desea igualdad lógica sobre igualdad de referencia". El consenso entre los diseñadores de idiomas fue probablemente lo contrario. Personalmente, me resulta difícil evaluar esto, ya que me falta el amplio espectro de experiencia en programación. Aproximadamente, uso la igualdad de referencia más en los algoritmos de optimización y la igualdad lógica más en el manejo de conjuntos de datos.
fuente
.equals()
compara variables por sus contenidos. en lugar de==
eso compara los objetos por su contenido ...usar objetos es más preciso para usar
.equals()
fuente