DEV Community

Cover image for Genericos en Go
Gino Luraschi
Gino Luraschi

Posted on

Genericos en Go

Introducción

Go ha ido evolucionando a través de los años, incluyendo herramientas interesantes en las últimas versiones. Una de estas herramientas son los genéricos, algo que ya existía en otros lenguajes de programación, pero no en Golang, y que es algo que podemos explotar para nuestro beneficio.

¿Qué son los genéricos?

Los genéricos permiten que nuestras funciones o estructuras de datos puedan ser creadas de diferentes tipos, que se definen en su forma genérica.

Ejemplo simple en java

En java, podemos tener como ejemplo los Arrays del paquete Utils:

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        Bag<Apple> applesBag = new Bag<>();
        applesBag.add(new Apple());
        applesBag.add(new Apple());
        applesBag.add(new Apple());
        System.out.println("Quantity of apples: " + applesBag.elementsQuantity());
        Bag<Orange> orangesBag = new Bag<>();
        orangesBag.add(new Orange());
        System.out.println("Quantity of oranges: " + applesBag.elementsQuantity());

    }

    static class Bag<K> {
        // Aca definimos una lista de K que estará en nuestra bolsa
        final List<K> listOfElements;

        public Bag(){
            listOfElements = new ArrayList<>();
        }

        public void add(K element) {
            listOfElements.add(element);
        }

        public int elementsQuantity(){
            return listOfElements.size();
        }
    }

    static class Apple {}
    static class Orange{}
}
Enter fullscreen mode Exit fullscreen mode

En nuestro ejemplo en java creamos una bolsa personalizada, en donde podemos poner lo que queramos del tipo K, en este caso nuestro K puede ser tanto Apple como Orange.

¿Se podía hacer antes?

En golang solo los tipos nativos como map, slices y punteros entre otros podian ser genéricos, por lo que podiamos crear por ejemplo un map[string]int o un []int, la forma de hacerlo antes, era crear un tipo para cada estructura:

package main

import "fmt"

func main() {
    applesBag := new(ApplesBag)
    applesBag.add(Apple{})
    applesBag.add(Apple{})
    applesBag.add(Apple{})
    fmt.Printf("Quantity of apples: %d\n", applesBag.elementsQuantity())
    orangesBag := new(OrangesBag)
    orangesBag.add(Orange{})
    fmt.Printf("Quantity of oranges: %d\n", orangesBag.elementsQuantity())
}

type ApplesBag struct {
    apples []Apple
}
type Apple struct{}

func (bag *ApplesBag) add(apple Apple) {
    bag.apples = append(bag.apples, apple)
}

func (bag *ApplesBag) elementsQuantity() int {
    return len(bag.apples)
}

type OrangesBag struct {
    oranges []Orange
}
type Orange struct{}

func (bag *OrangesBag) add(apple Orange) {
    bag.oranges = append(bag.oranges, apple)
}

func (bag *OrangesBag) elementsQuantity() int {
    return len(bag.oranges)
}
Enter fullscreen mode Exit fullscreen mode

De esta forma vemos como tenemos que crear un Bag para cada elemento, repitiendo código por cada tipo diferente que hagamos.

Como se hace ahora

Con la modificación que nos brinda la nueva SDK de Go, podemos hacer esto de una forma sencilla:

package main

import "fmt"

func main() {
    applesBag := Bag[Apple]{}
    applesBag.add(Apple{})
    applesBag.add(Apple{})
    applesBag.add(Apple{})
    fmt.Printf("Quantity of apples: %d\n", applesBag.elementsQuantity())
    orangesBag := Bag[Orange]{}
    orangesBag.add(Orange{})
    fmt.Printf("Quantity of oranges: %d\n", orangesBag.elementsQuantity())
}

// Tenemos una sola estructura Bag del tipo K
type Bag[K any] struct {
        // Que solo guarda objetos del tipo K
    objects []K
}

func (bag *Bag[K]) add(obj K) {
    bag.objects = append(bag.objects, obj)
}

func (bag *Bag[K]) elementsQuantity() int {
    return len(bag.objects)
}

type Orange struct{}
type Apple struct{}
Enter fullscreen mode Exit fullscreen mode

De esta manera, el día de mañana nuestro código queda lo suficientemente desacoplado para crear objetos e instancias de Bag, y no tener que crear un objeto y un bag para cada tipo.

Conclusión

Si en algún momento nos vimos con la necesidad de tomar este approach de Golang y no contábamos con esta solución, ahora vemos que llegó y podemos aprovecharla. Seguramente con el transcurso del tiempo empiezan a llegar nuevas herramientas igual de interesantes y útiles, seguimos a la espera de facilidades como las de manejar errores y otras cosillas más.

Top comments (0)