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: 25Tambié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.
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) yData(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 campoItemsque contiene un slice deItem[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) yPrice(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
Adden*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
CountenContainer[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,intopriced), 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 unContainer[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 unContainer[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 dePricedItem[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,intopriced) - 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 PaperTu salida debe ser:
Book: Fiction Novel
Pen: Blue Ink
Notebook: Lined Paper
Total items: 3Y dada la entrada:
int
2
Apples
50
Oranges
30Tu salida debe ser:
Apples: 50
Oranges: 30
Total items: 2Y dada la entrada:
priced
2
Widget
10
19.99
Gadget
5
49.50Tu salida debe ser:
Widget x10 @ 19.9
Gadget x5 @ 49.5
Total items: 2Observa 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
}
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
1Fundamentos de POO en Go
Archivos externosEspacio de trabajo y módulos de GoPaquetes e importacionesNombres exportados vs. no exportadosIntroducción a la POO en GoStructs como clasesDefinición de métodos en structsReceptores de puntero vs. de valorInicialización de structsFunciones constructorasResumen - Calculadora simple4Interfaces
Introducción a las interfacesImplementación implícitaLa interfaz como contratoInterfaz vacía (any)Type AssertionType SwitchComposición de interfacesInterfaces Stringer y ErrorResumen: Calculadora de figuras7Encapsulamiento
Campos exportados vs. no exportadosEncapsulamiento a nivel de paqueteMétodos Getter y SetterOcultamiento de información en GoResumen - Registros de estudiantes10Genéricos (Go 1.18+)
Introducción a los GenéricosParámetros de tipoRestricciones de tipoStructs genéricosSolución alternativa para métodos genéricosResumen - Colección genérica13Patrones de diseño - Parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón Abstract FactoryPatrón ObserverPatrón Strategy2Inmersión profunda en Tipos y Structs
Tipos básicos y compuestosDefiniciones de tipos personalizadosEtiquetas de StructsStructs anónimosStructs anidadosValores cero y por defectoRepaso - Libreta de contactos5Composición sobre herencia
Por qué Go no tiene herenciaConceptos básicos de incrustación de structsPromoción de métodosIncrustación de múltiples structsIncrustación vs AgregaciónShadowing de métodos incrustadosResumen - Jerarquía de empleados8Manejo de Errores y OOP
La interfaz errorTipos de errores personalizadosEnvoltura de errores (fmt.Errorf)Errores centinelaerrors.Is() y errors.As()Panic, Defer y RecoverResumen - Analizador de archivos3Punteros y Memoria
Fundamentos de punteros en GoPunteros a StructsPaso por valor vs referenciaLa función new()Garbage Collection en GoResumen - Constructor de Linked List6Polimorfismo en Go
Polimorfismo mediante interfacesDuck Typing en GoReglas de satisfacción de interfacesColecciones polimórficasInyección de dependenciasResumen - Procesador de pagos9Concurrencia y POO
Fundamentos de GoroutinesChannels y comunicaciónChannels con buffer vs. sin bufferSentencia Selectsync.Mutex y sync.RWMutexsync.WaitGroupDiseño de estructuras Thread-SafeResumen - Worker PoolPractica por tu cuenta: Compilador de Go online