Sobrecarga de Operadores

Tutorial Swift – Sobrecarga de Operadores

In Swift Avanzado, Tutoriales by Josué V. Herrera0 CommentsLast Updated: 05 Ago 2017

Hoy, en este Tutorial Swift abordaremos un tema bien importante, la sobrecarga de operadores. Para aquellos que no saben de lo que hablo y quieren aunque sea tener una idea antes de leer el artículo, pues les comento que la sobrecarga de operadores no es una característica propia de Swift, muchos lenguajes cuentan con esta y en C / C++ es bastante común su uso.

La Sobrecarga de Operadores nos permite vincular un Tipo con un Operador en pos de un comportamiento determinado.

Por ejemplo, los que hemos estudiado Java o C#, incluso los que conocemos Swift sabemos que esta es una expresión válida:

…la salida en pantalla sería:

Como bien dice el texto es un ejemplo de sobrecarga de operadores clásica, que está presente por defecto en la mayoría de los lenguajes. Hemos usado el operador de suma para concatenar cadenas de texto, en lugar de realizar la operación aritmética con la cual siempre lo asociamos, dando como resultado una cadena final donde todos estos segmentos confluyen tal y como podemos comprobar en la salida en pantalla.

¿Qué sucede cuando queremos lograr un comportamiento similar con un tipo propio?

Pues esto no lo podemos lograr con el comportamiento por defecto de los operadores, sobre todo porque el compilador no sabe el significado que tiene para nosotros esta ecuación. El lenguaje Swift no puede prever ni interpretar al vuelo que áreas de nuestras clases o estructuras deben de ser sumadas, concatenadas, o procesadas de cierta manera. Todos estos comportamientos tenemos que especificarlos nosotros.

Sobre todo esto hablaremos, haciendo uso de la flexibilidad que esto nos brinda  veremos como aplicar la sobrecarga de operadores a favor de tipos propios en pos de lograr los comportamientos deseados.

Una posible necesidad

Imaginemos que tenemos un tipo propio de nombre Product y que en este solamente almacenamos dos valores, el nombre del producto y el precio del mismo. Ahora necesitamos, a partir de una lista de compras, sumar todos los precios para informar al cliente cuando debe pagar por el total de sus compras. Este problema no representa ninguna complejidad, un solución pudiera ser:

…la salida en pantalla sería:

No me voy a detener mucho en este ejemplo, si tienen alguna duda me la dejan en los comentarios. Solamente haré referencia a las expresiones de la línea 24:

…y la línea 64:

En ambas líneas nos apoyamos en la propiedad constante precio de la estructura Product para así poder acceder a su valor, ya que si obviamos este punto y ejecutáramos algo como:

el compilador de Swift nos mostraría el siguiente error:

error: binary operator ‘-‘ cannot be applied to operands of type ‘Product’ and ‘Double’

…en el cual se nos informa que el operador binario de resta (-) no puede ser usado cuando sus operandos son de tipo Product o Double, y esto no tiene nada que ver con Double, la razón es que el compilador no sabe como interactuar con Product en el caso de una suma, una resta o cualquier otra operación aritmética.

¿Qué deseamos lograr?

Con todo cuanto hemos visto hasta ahora en nuestra mente y a sabiendas de que no tenemos ningún error, pasemos a implementar una sobrecarga de operadores que nos permita simplificar un poco el código que hasta ahora tenemos. Haciendo esto nuestras instancias de tipo Product podrán ser usadas en operaciones aritméticas logrando expresiones como la siguiente:

…veamos como lograrlo, no sin antes una pequeña introducción técnica.

La sobrecarga de operadores en Swift

Lo primero que debemos de conocer son los siguientes términos que agrupan a los operadores:

  • Prefix: El operador unario aparecerá antes del operando (Ejemplo: +=, -=, !, ~…)
  • Infix: El operador binario aparecerá en el medio de los dos operandos (Ejemplo: +, -, *, &+, &-…)
  • Postfix: El operador unario aparecerá después del operando (Ejemplo: &, ?…)

Conociendo ya la utilidad de la sobrecarga de operadores y lo que nos permite lograr veamos como luce una sobrecarga del operador de multiplicación (*), un ejemplo sencillo:

Aquí podemos percatarnos una vez más sobre eso que comentan que en Swift todo (casi todo) es una función o se logra con una función. El nombre de esta función es el operador al que le estamos agregando una nueva interacción y dentro de los paréntesis tenemos a los dos operandos que interactúan con este operador binario.

Para los nombres de estos operandos hemos usado un término matemático (lhs y rhs) que usualmente se usa para hacer referencia a estos, en otras palabras viene siendo ya como una norma pero pudieran tener cualquier nombre (Left, Right…). En el caso de la derecha el nombre lhs son las siglas en inglés para left-hand side y rhs para right-hand side, que en castellano se traduciría como lado izquierdo y lado derecho. A cada uno de estos se le especifica su tipo de dato dejando claro al compilador que esta sobrecarga está enfocada en el lado izquierdo a un tipo String y en el derecho a un Int, para finalizar devolviendo una cadena String.

Podemos resumir lo que acabamos de comentar en la siguiente tabla:

ÁreaOperando IzquierdoOperadorOperando Derecho
Infix2+3
Prefix+=5
PostfixisEmpty()!

Continuando con el ejemplo anterior donde sobrecargábamos al operador de multiplicación,  este código nos permite escribir la siguiente expresión:

…algo que no tendría sentido a no ser que nuestra intención sea multiplicar someString cinco veces, una necesidad bien especifica y rara cuya sobrecarga del operador de multiplicación tiene que ser provista por nosotros. El ejemplo completo:

…la salida en pantalla sería:

La implementación

En este punto ya con todo mucho más claro, retomaremos el ejemplo del inicio e implementaremos una sobrecarga sobre nuestro tipo de dato Product.

Para esto comenzaremos afrontando el caso:

…el código asociado sería:

Comenzamos haciendo uso de la palabra clave static ya que este segmento de código se encuentra dentro de la clase Product. Luego continuamos declarando nuestro operando de la izquierda, que vendría a ser la variable totalAPagar la cual recibe el retorno de la expresión. Como deben de haberse dado cuenta, este operando ha sido marcado como inout ya que no queremos trabajar con una copia de este, más bien con una referencia a su posición de memoria para poder almacenar el valor de retorno.

El otro operando (rhs) sería la instancia de Product que como podemos leer en la línea 3 hacemos uso de la propiedad precio que es donde se almacena el valor de este producto. Esta sobrecarga viene siendo como una especie de enmascaramiento de la operación que hacíamos previamente.

El otro caso que nos interesa es:

…y para que esta expresión sea válida necesitaríamos añadir a la clase Product el código:

Como podemos constatar el código que antes formaba parte del bucle for ahora se encuentra segmentado bajo la operación matemática de la resta siempre y cuando los operandos sean en el lado izquierdo una instancia de Product y en el derecho una instancia de Discount.

La estructura Product ahora luce así:

…mientras que el aspecto del área correspondiente al bucle for:

Aquí podemos observar de que hay varias líneas comentadas y esto es debido a que los últimos cambios que hemos efectuado no evitan que la antigua versión del código siga funcionando. Ya queda del lado del programador cual de las dos versiones usar, cual de estas le resulta más legible y menos propensa a errores.

…la nueva salida en pantalla sería:

Conclusiones

Ya en este punto conocemos todo cuanto se puede hacer con la sobrecarga de operadores y nos damos cuenta de que siempre hemos interactuado con ella. Es evidente que detrás de cada lenguaje de programación hay un análisis léxico que interpreta las expresiones que escribimos, solamente con poner 5 + 5 no se genera de manera mágica el número 10, detrás de esta operación hay una sobrecarga del operador de suma (+) para el caso lhs (Int) y rhs (Int).

La expresión 5 + 5 no genera un 10 por arte de magia, es un caso lhs (Int) y rhs (Int), una sobrecarga por defecto del operador de suma (+).

Dicho esto queda aún más clara la importancia de la sobrecarga de operadores no solo cuando hablamos de los operadores que ya conocemos, también cuando necesitamos crear operadores que ejecuten tareas personalizadas, implementados con funcionalidades genérica en pos de la interacción con varios tipos de datos.

Una lista de las sobrecargas de operadores que Swift aplica por defecto la podemos encontrar en el siguiente link (Inglés):

Swift Standard Library Operators

El código de ejemplo lo pueden encontrar en un proyecto Playground alojado en la cuenta personal de Josué V. Herrera en GitHub, específicamente en el repositorio asociado a este artículo.

Espero que todo cuanto se ha dicho aquí, de una forma u otra le haya servido de aprendizaje, de referencia, que haya valido su preciado tiempo.

Este artículo, al igual que el resto, será revisado con cierta frecuencia en pos de mantener un contenido de calidad y actualizado.

Cualquier sugerencia, ya sea errores a corregir, información o ejemplos a añadir será, más que bienvenida, necesaria!

Acerca de Josué V. Herrera

Ciudadano del mundo. Me gustan mucho los animales y la naturaleza, la literatura, la música y el cine, aficionado a la aviación y a todo lo que es tecnología y ciencia. Desde el 2005 me dedico de manera profesional a la informática, trabajando principalmente como administrador de redes y sistemas aunque también he fungido como programador, especializándome en Linux y en C++ / Qt. Soy Experto en Administración y Seguridad de Redes por la Universidad Tecnológica Nacional FRVM de Argentina. Desde el 2014 me dedico al estudio de Swift y el entorno que lo rodea, ya sea desde el lado de Apple como del Open Source.

Ver Todos