Menu
Coddy logo textTech

Structs genéricos

Parte de la sección Programación Orientada a Objetos del Journey de GO de Coddy. Lección 70 de 107.

Al igual que las funciones, las estructuras también pueden tener parámetros de tipo. Una estructura genérica te permite crear estructuras de datos que funcionan con cualquier tipo, manteniendo la seguridad de tipos. Esto es perfecto para crear contenedores reutilizables, como pilas, colas o pares clave-valor.

Para definir una estructura genérica, coloca el parámetro de tipo después del nombre de la estructura:

type Box[T any] struct {
    Value T
}

func main() {
    intBox := Box[int]{Value: 42}
    strBox := Box[string]{Value: "hello"}
    
    fmt.Println(intBox.Value)  // 42
    fmt.Println(strBox.Value)  // hello
}

A diferencia de las funciones genéricas, debes especificar explícitamente el tipo al crear una instancia de una estructura genérica. Go no puede inferir el tipo basándose únicamente en el contexto.

Las estructuras genéricas pueden tener múltiples parámetros de tipo, lo que las hace ideales para estructuras como pares o mapas:

type Pair[K, V any] struct {
    Key   K
    Value V
}

p := Pair[string, int]{Key: "age", Value: 25}
fmt.Printf("%s: %d\n", p.Key, p.Value)  // age: 25

También puedes aplicar restricciones para asegurar que los tipos almacenados admitan operaciones específicas:

type NumberBox[T int | float64] struct {
    Value T
}

nb := NumberBox[float64]{Value: 3.14}

Las estructuras genéricas forman la base para construir estructuras de datos reutilizables y con seguridad de tipos en Go. En la próxima lección, aprenderás a definir métodos en estos tipos genéricos.

challenge icon

Desafío

Fácil

¡Construyamos un sistema de inventario genérico que pueda realizar un seguimiento de diferentes tipos de artículos! Crearás estructuras de contenedor reutilizables que funcionen con cualquier tipo y mantengan la seguridad de tipos completa.

Organizarás tu código en dos archivos:

  • inventory.go: Define tus estructuras de contenedor genéricas.

    Crea una estructura genérica Item[T any] con dos campos: Name (string) y Data (de tipo T). Esto representa cualquier artículo con datos asociados de un tipo flexible.

    Crea una estructura genérica Container[T any] con un único campo Items que contiene un slice de Item[T]. Este contenedor puede almacenar varios artículos del mismo tipo de datos.

    Crea una estructura genérica restringida PricedItem[T int | float64] con tres campos: Name (string), Quantity (int) y Price (de tipo T). La restricción garantiza que los precios siempre sean numéricos.

    Implementa una función NewContainer[T any]() *Container[T] que cree y devuelva un puntero a un Container vacío.

    Implementa un método Add en *Container[T] que reciba un nombre (string) y datos (T), cree un Item y lo añada al slice Items del contenedor.

    Implementa un método Count en Container[T] que devuelva el número de artículos del contenedor.

  • main.go: Demuestra tus estructuras genéricas con diferentes tipos.

    Lee un tipo de artículo (string, int o priced), después lee una cantidad seguida de los detalles de los artículos.

    Para el tipo string: Lee pares de nombre y datos de tipo string. Crea un Container[string], añade todos los artículos y después imprime cada artículo como [Name]: [Data], seguido del recuento total.

    Para el tipo int: Lee pares de nombre y datos enteros. Crea un Container[int], añade todos los artículos y después imprime cada artículo como [Name]: [Data], seguido del recuento total.

    Para el tipo priced: Lee tríos de nombre, cantidad y precio (como float). Crea directamente instancias de PricedItem[float64] e imprime cada una como [Name] x[Quantity] @ [Price], mostrando el precio con un decimal.

    Imprime la línea del recuento como: Total items: [count]

Se proporcionarán las siguientes entradas:

  • Línea 1: Tipo de artículo (string, int o priced)
  • Línea 2: Cantidad de artículos (entero)
  • Líneas siguientes: Detalles de los artículos según el tipo

Por ejemplo, dada la entrada:

string
3
Book
Fiction Novel
Pen
Blue Ink
Notebook
Lined Paper

Tu salida debe ser:

Book: Fiction Novel
Pen: Blue Ink
Notebook: Lined Paper
Total items: 3

Y dada la entrada:

int
2
Apples
50
Oranges
30

Tu salida debe ser:

Apples: 50
Oranges: 30
Total items: 2

Y dada la entrada:

priced
2
Widget
10
19.99
Gadget
5
49.50

Tu salida debe ser:

Widget x10 @ 19.9
Gadget x5 @ 49.5
Total items: 2

Observa cómo la misma estructura Container funciona perfectamente tanto con strings como con enteros, mientras que PricedItem utiliza una restricción para garantizar que solo se puedan usar tipos numéricos para los precios. Debes especificar explícitamente el parámetro de tipo al crear instancias de estas estructuras genéricas.

Pruébalo tú mismo

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
)

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	
	// Leer el tipo de elemento
	scanner.Scan()
	itemType := scanner.Text()
	
	// Leer la cantidad
	scanner.Scan()
	count, _ := strconv.Atoi(scanner.Text())
	
	switch itemType {
	case "string":
		// TODO: Crear un Container[string] usando NewContainer
		// TODO: Leer 'count' elementos (cada uno tiene el nombre en una línea y los datos en la siguiente)
		// TODO: Agregar cada elemento al contenedor
		// TODO: Imprimir cada elemento como "[Name]: [Data]"
		// TODO: Imprimir "Total items: [count]" usando el método Count
		
	case "int":
		// TODO: Crear un Container[int] usando NewContainer
		// TODO: Leer 'count' elementos (cada uno tiene el nombre en una línea y los datos enteros en la siguiente)
		// TODO: Agregar cada elemento al contenedor
		// TODO: Imprimir cada elemento como "[Name]: [Data]"
		// TODO: Imprimir "Total items: [count]" usando el método Count
		
	case "priced":
		// TODO: Crear un slice para contener instancias de PricedItem[float64]
		// TODO: Leer 'count' elementos (cada uno tiene nombre, cantidad y precio en líneas separadas)
		// TODO: Crear un PricedItem[float64] para cada uno y agregarlo al slice
		// TODO: Imprimir cada elemento como "[Name] x[Quantity] @ [Price]" con el precio mostrando un decimal
		// TODO: Imprimir "Total items: [count]"
	}
	
	_ = scanner // Usar scanner para leer la entrada
	_ = count   // Usar count para el bucle
}
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación Orientada a Objetos

Practica por tu cuenta: Compilador de Go online