Struct generiche
Fa parte della sezione Programmazione orientata agli oggetti del percorso GO di Coddy. Lezione 70 di 107.
Proprio come le funzioni, anche le struct possono avere parametri di tipo. Una struct generica ti permette di creare strutture dati che funzionano con qualsiasi tipo, mantenendo la sicurezza dei tipi. È perfetta per creare contenitori riutilizzabili come pile, code o coppie chiave-valore.
Per definire una struct generica, inserisci il parametro di tipo dopo il nome della struct:
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 differenza delle funzioni generiche, devi specificare esplicitamente il tipo quando crei un’istanza di una struct generica. Go non può dedurre il tipo dal solo contesto.
Le struct generiche possono avere più parametri di tipo, il che le rende ideali per strutture come coppie o mappe:
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: 25Puoi anche applicare vincoli per assicurarti che i tipi memorizzati supportino operazioni specifiche:
type NumberBox[T int | float64] struct {
Value T
}
nb := NumberBox[float64]{Value: 3.14}Le struct generiche costituiscono le fondamenta per creare strutture dati riutilizzabili e sicure rispetto ai tipi in Go. Nella prossima lezione imparerai a definire metodi su questi tipi generici.
Sfida
FacileCostruiamo un sistema di inventario generico in grado di tenere traccia di diversi tipi di articoli! Creerai strutture contenitore riutilizzabili che funzionano con qualsiasi tipo, mantenendo la piena sicurezza dei tipi.
Organizzerai il codice in due file:
inventory.go: Definisci le tue strutture contenitore generiche.Crea una struct generica
Item[T any]con due campi:Name(string) eData(di tipo T). Rappresenta qualsiasi articolo con dati associati di tipo flessibile.Crea una struct generica
Container[T any]con un singolo campoItemsche contiene uno slice diItem[T]. Questo contenitore può conservare più articoli dello stesso tipo di dati.Crea una struct generica vincolata
PricedItem[T int | float64]con tre campi:Name(string),Quantity(int) ePrice(di tipo T). Il vincolo garantisce che i prezzi siano sempre numerici.Implementa una funzione
NewContainer[T any]() *Container[T]che crea e restituisce un puntatore a un Container vuoto.Implementa un metodo
Addsu*Container[T]che accetta un nome (string) e dei dati (T), crea un Item e lo aggiunge allo slice Items del contenitore.Implementa un metodo
CountsuContainer[T]che restituisce il numero di articoli nel contenitore.main.go: Mostra le tue strutture generiche con tipi diversi.Leggi un tipo di articolo (
string,intoppurepriced), quindi leggi un conteggio seguito dai dettagli degli articoli.Per il tipo
string: Leggi coppie di nome e dati stringa. Crea unContainer[string], aggiungi tutti gli articoli, quindi stampa ciascun articolo come[Name]: [Data]seguito dal conteggio totale.Per il tipo
int: Leggi coppie di nome e dati interi. Crea unContainer[int], aggiungi tutti gli articoli, quindi stampa ciascun articolo come[Name]: [Data]seguito dal conteggio totale.Per il tipo
priced: Leggi terne di nome, quantità e prezzo (come float). Crea direttamente istanze diPricedItem[float64]e stampa ciascuna come[Name] x[Quantity] @ [Price], mostrando il prezzo con una cifra decimale.Stampa la riga del conteggio nel formato:
Total items: [count]
Verranno forniti i seguenti input:
- Riga 1: Tipo di articolo (
string,intoppurepriced) - Riga 2: Numero di articoli (intero)
- Righe successive: Dettagli degli articoli in base al tipo
Per esempio, dato:
string
3
Book
Fiction Novel
Pen
Blue Ink
Notebook
Lined PaperL'output dovrebbe essere:
Book: Fiction Novel
Pen: Blue Ink
Notebook: Lined Paper
Total items: 3E dato:
int
2
Apples
50
Oranges
30L'output dovrebbe essere:
Apples: 50
Oranges: 30
Total items: 2E dato:
priced
2
Widget
10
19.99
Gadget
5
49.50L'output dovrebbe essere:
Widget x10 @ 19.9
Gadget x5 @ 49.5
Total items: 2Nota come la stessa struttura Container funzioni senza problemi sia con le stringhe sia con gli interi, mentre PricedItem usa un vincolo per garantire che per i prezzi si possano usare solo tipi numerici. Quando crei istanze di queste struct generiche, devi specificare esplicitamente il parametro di tipo.
Provalo tu
package main
import (
"bufio"
"fmt"
"os"
"strconv"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// Leggi il tipo di elemento
scanner.Scan()
itemType := scanner.Text()
// Leggi count
scanner.Scan()
count, _ := strconv.Atoi(scanner.Text())
switch itemType {
case "string":
// TODO: Crea un Container[string] usando NewContainer
// TODO: Leggi 'count' elementi (ognuno ha il nome su una riga e i dati sulla riga successiva)
// TODO: Aggiungi ogni elemento al contenitore
// TODO: Stampa ogni elemento nel formato "[Name]: [Data]"
// TODO: Stampa "Total items: [count]" usando il metodo Count
case "int":
// TODO: Crea un Container[int] usando NewContainer
// TODO: Leggi 'count' elementi (ognuno ha il nome su una riga e i dati interi sulla riga successiva)
// TODO: Aggiungi ogni elemento al contenitore
// TODO: Stampa ogni elemento nel formato "[Name]: [Data]"
// TODO: Stampa "Total items: [count]" usando il metodo Count
case "priced":
// TODO: Crea una slice per contenere istanze di PricedItem[float64]
// TODO: Leggi 'count' elementi (ognuno ha nome, quantità e prezzo su righe separate)
// TODO: Crea un PricedItem[float64] per ciascuno e aggiungilo alla slice
// TODO: Stampa ogni elemento nel formato "[Name] x[Quantity] @ [Price]", mostrando il prezzo con una cifra decimale
// TODO: Stampa "Total items: [count]"
}
_ = scanner // Usa scanner per leggere l'input
_ = count // Usa count per il ciclo
}
Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.
Tutte le lezioni di Programmazione orientata agli oggetti
1Fondamenti di OOP in Go
File esterniWorkspace e moduli GoPacchetti e importazioniNomi esportati e non esportatiIntroduzione all’OOP in GoGli struct come classiDefinire metodi sugli structReceiver con puntatore vs receiver con valoreInizializzazione degli structFunzioni costruttriciRiepilogo - Calcolatrice semplice4Interfacce
Introduzione alle interfacceImplementazione implicitaL’interfaccia come contrattoInterfaccia vuota (any)Asserzione di tipoSwitch sul tipoComposizione di interfacceInterfacce Stringer ed ErrorRipasso - Calcolatore di forme7Incapsulamento
Campi esportati e non esportatiIncapsulamento a livello di packageMetodi Getter e SetterNascondimento delle informazioni in GoRiepilogo - Registri degli studenti10Tipi generici (Go 1.18+)
Introduzione ai tipi genericiParametri di tipoVincoli di tipoStruct genericheSoluzione alternativa per i metodi genericiRiepilogo - Collezione generica13Modelli di progettazione Parte 1
Introduzione ai modelli di progettazioneModello SingletonModello FactoryModello Abstract FactoryModello ObserverModello Strategy2Approfondimento su tipi e struct
Tipi di base e compostiDefinizioni di tipi personalizzatiTag delle structStruct anonimeStruct annidateValori zero e predefinitiRipasso - Rubrica5Composizione invece dell'ereditarietà
Perché Go non ha l'ereditarietàFondamenti dell'embedding delle structPromozione dei metodiEmbedding di più structEmbedding e aggregazione a confrontoMascheramento dei metodi embeddedRiepilogo: gerarchia dei dipendenti8Gestione degli errori e OOP
L'interfaccia errorTipi di errore personalizzatiWrapping degli errori (fmt.Errorf)Errori sentinellaerrors.Is() ed errors.As()Panic, Defer e RecoverRiepilogo - Parser di file11Libreria standard e OOP
io.Reader e io.Writersort.InterfaceInterfaccia fmt.Stringerencoding/json con le structInterfaccia http.HandlerRipasso - modelli REST API14Modelli di progettazione – Parte 2
Pattern CommandPattern AdapterPattern DecoratorPattern Template MethodPattern StatePattern CompositeMiddleware come Decorator3Puntatori e memoria
Concetti base dei puntatori in GoPuntatori a structPassaggio per valore vs per riferimentoLa funzione new()Garbage collection in GoRiepilogo - Costruttore di liste concatenate6Polimorfismo in Go
Polimorfismo tramite interfacceDuck typing in GoRegole di soddisfacimento delle interfacceCollezioni polimorficheIniezione delle dipendenzeRiepilogo - Elaboratore di pagamenti9Concorrenza e OOP
Basi delle goroutineCanali e comunicazioneCanali bufferizzati vs non bufferizzatiIstruzione selectsync.Mutex e sync.RWMutexsync.WaitGroupProgettazione di struct thread-safeRiepilogo: worker poolEsercitati da solo: Compilatore Go online