El operador de igualdad se usa para dos conceptos diferentes en los lenguajes de programación: la identidad y la equivalencia. En este artículo quiero aclarar estos conceptos y dar ejemplos como algunos lenguajes de programación los implementan.

Los conceptos

Habitualmente se denomina igualdad al operador == (o = en algunos lenguajes como Pascal o Visual Basic). Pero hay dos conceptos de lo que puede ser igual: la entidad que contiene un valor (identidad) o el valor que contiene (equivalencia).

Decimos que las dos variables a = 5 y b = 5 son iguales en sentido matemático porque contienen el mismo valor 5. Sin embargo, a y b son dos variables distintas en sentido informático si se guardan en dos direcciones distintas en la memoria. En el último caso hablamos de identidad: las variables a y b son iguales cuando hacen referencia a la misma entidad. En el primer caso hablamos de equivalencia. Las variables pueden hacer referencia a una memoria diferente, pero contienen el mismo valor.

Es obvio que la identidad es un criterio más estricto. Dos objetos distintos pueden tener el mismo valor o no, pero el mismo objeto comparado a si mismo siempre es igual.

El uso en los lenguajes

Implementación de identidad y equivalencia
Significado de igualdad
Equivalencia
(mismo valor)
Identidad
(mismo objeto)
Lenguajes compiladas (“antiguas”) Operador == Operador == entre punteros
Lenguajes interpretadas (“modernas”) Método equals Operador ==

Los lenguajes de programación más antiguas como C o Fortran suelen interpretar la igualdad como equivalencia. Para esto puede haber varias razones. Primero la equivalencia es el concepto más matemático (Fortran viene de formula translation). Luego no existía el concepto de objeto cuando se crearon estos lenguajes. Los tipos de variables eran los básicos como caracteres, números enteros y números de coma flotante. Los compiladores convertieron las expresiones a == b a una comparación entre los valores de dos direcciones de memoria. Para comprobar identidad se podían comparar los punteros a estas memorias. No extraña que se sobrecarga el operador == en C++ con un significado de equivalencia hasta hoy en día.

El asunto se cambió con los lenguajes orientado a objetos en que no existen punteros como C#, Java o PHP. En estos lenguajes, no son las variables que guardan los objetos sino el intérprete. La variable de objeto guarda meramente un identificador. Por lo tanto, una comparación entre dos variables es una comparación entre identificadores y no valores. En consecuencia la igualdad tiene la conotación de identidad.

También cabe señalar que estos lenguajes no permiten sobrecarga el operador == como en C++. No es posible crear un operador == que compare el contenido. Tampoco conviene que el intérprete realice una comparación binaria entre dos objetos. La representación binaria depende de la plataforma y un lenguaje como Java se creó justamente para no depender de ella.

Clases inmutables

Ninguna regla sin excepción. En Java o C#, sí, se pueden comparar los valores de dos strings con el operador ==. Esto es posible ya que estas clases son inmutables (o constantes según la jerga de C++): Sus instancias ya no se pueden modificar una vez creadas. Los intérpretes – la máquina virtual de Java o el framework de .NET, respectivamente – aprovechan esta característica. Si initializo una instancia de string con un valor ya existente en otra instancia, entonces no se crea un nuevo valor, sino la nueva instancia apunta al valor existente. Por esto, la nueva variable contiene una referencia a la misma instancia que la variable antigua. Ambas apuntan al mismo objeto y, por lo tanto, son idénticos.

Las clases inmutables tienen más características interesantes. Por ejemplo, no hace falta copiar sus contenidos. En una copia profunda de una clase (deep copy en inglés) debo crear constructores de copia para cada subobjeto de una clase, pero no para un string. La copia causa que una variable de clase string apunta a otro objeto guardado en el intéprete, pero el valor del objeto en si no se modifica ya que es inmutable. Operaciones sobre cadenas de texto reasignan referencias, pero no tiene por qué crear nuevas cadenas.

Conclusión

Hemos visto que existen dos conceptos de igualdad: la identidad y la equivalencia. La equivalencia es el concepto implementado en los lenguajes más tradicionales, mientras la identidad se ha impuesto en los lenguajes más recientes – entre otros motivos por razones técnicas. Los lenguajes que implementan el operador == como equivalencia suelen comparar identidades mediante punteros. Los lenguajes que implementan la identidad para el operador == suelen propicionar un método equals para la equivalencia.

Enlaces de interés