Introduzione ai tipi generici
Fa parte della sezione Programmazione orientata agli oggetti del percorso GO di Coddy. Lezione 67 di 107.
Prima di Go 1.18, scrivere codice riutilizzabile per tipi diversi significava duplicare le funzioni oppure usare l’interfaccia vuota any con asserzioni di tipo. I generics risolvono questo problema permettendoti di scrivere funzioni e tipi che funzionano con più tipi mantenendo la piena sicurezza dei tipi.
Considera di trovare il valore minimo in una slice. Senza i generici, avresti bisogno di funzioni separate per ogni tipo:
func MinInt(values []int) int {
min := values[0]
for _, v := range values {
if v < min {
min = v
}
}
return min
}
func MinFloat64(values []float64) float64 {
min := values[0]
for _, v := range values {
if v < min {
min = v
}
}
return min
}Con i generics, scrivi la logica una sola volta usando un parametro di tipo tra parentesi quadre:
func Min[T int | float64](values []T) T {
min := values[0]
for _, v := range values {
if v < min {
min = v
}
}
return min
}[T int | float64] dichiara un parametro di tipo T che può essere int oppure float64. Ora puoi chiamare Min([]int{3, 1, 2}) oppure Min([]float64{3.5, 1.2}) con la stessa funzione. Il compilatore controlla i tipi in fase di compilazione, così ottieni sicurezza senza overhead a runtime.
Nelle prossime lezioni esploreremo in dettaglio i parametri di tipo, i vincoli e le struct generiche.
Sfida
FacileCostruiamo un analizzatore di punteggi che funziona sia con punteggi interi sia con numeri in virgola mobile, usando i generics! Creerai un'unica funzione generica in grado di trovare il valore massimo in una slice, eliminando la necessità di duplicare il codice per tipi numerici diversi.
Organizzerai il codice in due file:
analyzer.go: definisci la tua funzione generica di analisi.Crea una funzione generica
Max[T int | float64](values []T) Tche trova e restituisce il valore massimo in una slice. La funzione deve funzionare sia con i tipiintsia confloat64tramite il parametro di tipoT.Crea anche una funzione generica
Sum[T int | float64](values []T) Tche calcola e restituisce la somma di tutti i valori in una slice.main.go: leggi l'input e mostra le tue funzioni generiche con tipi diversi.Leggi un indicatore di tipo (
intofloat), poi leggi un conteggio seguito da altrettanti valori. Analizza i valori in base al tipo, chiama siaMaxsiaSumcon il tipo appropriato e stampa i risultati.Per un input intero, stampa:
Max (int): [value] Sum (int): [value]Per un input in virgola mobile, stampa con una cifra decimale:
Max (float): [value] Sum (float): [value]
Verranno forniti i seguenti input:
- Riga 1: indicatore di tipo (
intofloat) - Riga 2: conteggio dei valori (intero)
- Righe successive: un valore per riga
Per esempio, dato:
int
4
15
8
23
11Il tuo output dovrebbe essere:
Max (int): 23
Sum (int): 57E dato:
float
3
4.5
9.2
6.8Il tuo output dovrebbe essere:
Max (float): 9.2
Sum (float): 20.5Nota come le stesse funzioni generiche gestiscano sia i dati interi sia quelli in virgola mobile: il parametro di tipo [T int | float64] consente al compilatore di generare codice sicuro rispetto ai tipi per ogni utilizzo, mentre tu scrivi la logica una sola volta.
Provalo tu
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Leggi l'indicatore del tipo
typeStr, _ := reader.ReadString('\n')
typeStr = strings.TrimSpace(typeStr)
// Leggi il conteggio
countStr, _ := reader.ReadString('\n')
count, _ := strconv.Atoi(strings.TrimSpace(countStr))
if typeStr == "int" {
// Leggi i valori interi
values := make([]int, count)
for i := 0; i < count; i++ {
line, _ := reader.ReadString('\n')
values[i], _ = strconv.Atoi(strings.TrimSpace(line))
}
// TODO: Chiama Max e Sum con una slice di interi
// TODO: Stampa i risultati nel formato "Max (int): [value]" e "Sum (int): [value]"
} else if typeStr == "float" {
// Leggi i valori in virgola mobile
values := make([]float64, count)
for i := 0; i < count; i++ {
line, _ := reader.ReadString('\n')
values[i], _ = strconv.ParseFloat(strings.TrimSpace(line), 64)
}
// TODO: Chiama Max e Sum con una slice di float64
// TODO: Stampa i risultati con una cifra decimale nel formato "Max (float): [value]" e "Sum (float): [value]"
}
}
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