Interfaces vacías en Go (Golang)

in #cervantes6 years ago

gopher wallpaper

En la publicación anterior conocimos las interfaces, un tipo de dato de Go bastante interesante. Si embargo, una sola publicación no fue suficiente para cubrir lo esencial de este tipo de dato, por lo que decidí dedicar el presente artículo a otros detalles sobre las interfaces

Interfaces vacías en Go

Muchas veces nos enfrentaremos a situaciones en las que, por algún motivo, desconocemos el tipo de dato con el que vamos a tratar o simplemente queremos manejar diferentes tipos de datos. Siendo Go un lenguaje de tipado estático esta situación parece difícil de abordar, pero las interfaces pueden ayudarnos. Hace algún tiempo, cuando conocimos los mapas en go les di un pequeño adelanto, permitan que reutilice ese ejemplo.

package main
import "fmt"
func main() {
        mapa := make(map[string]interface{})
        mapa["primero"] = 4
        mapa["segundo"] = false
        mapa["tercero"] = "Steemit"
        fmt.Println(mapa) // map[segundo:false tercero:Steemit primero:4]
}



Cómo pueden ver, podemos usar una interfaz vacía interface{} como un tipo de dato y este es compatible con cualquier tipo de dato. Seguramente se les ocurren muchas posibilidades con esto, en especial si vienes de lenguajes de tipado dinámico, por ejemplo lo siguiente.

…
func main() {
        mapa := make(map[string]interface{})
        mapa["primero"] = 4
        mapa["segundo"] = false
        mapa["tercero"] = "Steemit"
        fmt.Println(mapa["primero"] + 8)
}



Siendo el valor correspondiente a la clave ”primero”, un número entero, parece lógico suponer que podremos realizar cualquier operación propia de este tipo de dato, por ejemplo sumarlo con otro número entero, sin embargo, la operación mapa["primero"] + 8 generará un error en tiempo de compilación, porque intentas sumar dos tipos diferentes (interface{} y int), esto podría parecer extraño a primera vista, pero recordemos que Go es un lenguaje de tipado estricto y no realiza conversiones implícitas de tipos. Lo que probablemente nos lleve a pensar en realizar int(mapa["primero"]) + 8, si ese fue el caso, felicitaciones, has estado prestando atención, pero siento decirte que tampoco funcionará.

Type assertion

El type assertion nos permite acceder a un valor de un tipo concreto de una interfaz, creo que es más fácil mostrarlo que explicarlo: mapa["primero"].(int). Sí, es necesaria esa curiosa sintaxis de punto y el tipo entre paréntesis, con esto recibimos el valor de la interfaz convertido al tipo indicado, es decir, el ejemplo anterior funcionaría se lo escribimos de la siguiente manera.

package main
import "fmt"
func main() {
        mapa := make(map[string]interface{})
        mapa["primero"] = 4
        mapa["segundo"] = false
        mapa["tercero"] = "Steemit"
        fmt.Println(mapa["primero"].(int) + 8) // 12
}



Perfecto, ya funciona, podríamos dejar el tema hasta aquí, pero aún existe un inconveniente que me gustaría solventar. Si el tipo indicado en las aserción no es es compatible con el valor de la interface obtendremos un panic, me parece que no hemos hablando de panic pero pueden considerarlo un error fatal que detendrá su aplicación si no es manejado. Me parece que no queremos que eso suceda, así que les mostraré algunas opciones.

Validando la aserción

Es posible, de forma similar a como lo hacemos con los mapas, comprobar que una aserción sea o no válida para así poder tomar acciones en consecuencia. Esto se logra gracias a que la aserción puede retornar dos valores, el valor del tipo indicado y un bool que indica si las aserción fue exitosa.

...
if v, ok := mapa["primero"].(int); ok {
        fmt.Println(v + 8) // 12
}


Type switches

Si necesitamos comprobar múltiples tipos para tomar acciones según esto, lo mas adecuado es usar la modalidad del switch para comprobar los tipos, de esta manera resulta bastante sencillo

package main
import "fmt"
func main() {
        mapa := make(map[string]interface{})
        mapa["primero"] = 4
        mapa["segundo"] = false
        mapa["tercero"] = "Steemit"
        for _, i := range mapa {
                switch i.(type) {
                case string:
                        fmt.Println("Es un string")
                case int:
                        fmt.Println("Es un int")
                case bool:
                        fmt.Println("Es un bool")
                default:
                        fmt.Println("No sé que sea")
                }
        }
}



Espero que les pareciera interesante, sinceramente es una práctica que intento evitar, pero en ocasiones es muy útil, así que espero puedan sacarle provecho. Si han decidido embarcarse en el la aventura de aprender el lenguaje de programación Go, les aseguro ocuparan mucho las interfaces.

separator.png

Publicaciones relacionadas

  1. De Python a Go (Golang)

  2. Introducción al lenguaje de programación Go (Golang)

  3. Estructuras de control de flujo en Go

  4. Array y Slice en Go (Golang)

  5. Maps en Go (Golang)

  6. Punteros en Go (Golang)

  7. Importaciones y paquetes en Go (Golang)

  8. Paquetes de terceros y alias en Go (Golang)

  9. Tipos de datos Personalizados y Métodos en Go (Golang)

  10. Estructuras: ¿Las Clases de Go (Golang)?

  11. Interfaces en Go (Golang)

Gracias por leer, espero que este artículo te resultara de provecho. Si así fue, no dudes en dejar un comentario, compartirlo y votar. Te invito a comentar cualquier duda o sugerencia, te aseguro que las leo todas. Así que, por favor, ayúdame a mejorar y continuar compartiendo contenido de calidad. Si te gusta la programación y/o la informática en general, te invito a formar parte de la comunidad Develop Spanish dónde compartimos contenido de esa naturaleza y totalmente en español. Hasta la próxima.

banner-steemit.jpg