# Interfacce Go Avanzate nel 2026: Composizione, Type Assertion e Domande da Colloquio > Una guida completa alle interfacce Go: composizione tramite embedding, type assertion sicure, generics autoreferenziali in Go 1.26 e le domande più frequenti nei colloqui tecnici per sviluppatori senior. - Published: 2026-09-10 - Updated: 2026-09-10 - Author: Anthony Fillion-Maillet - Tags: go, interfaces, type assertions, generics, interview - Reading time: 5 min --- Le interfacce Go definiscono comportamenti senza prescrivere implementazioni, rendendole centrali per scrivere codice flessibile e testabile. Go 1.26 estende questa potenza con generics autoreferenziali, nuovi iteratori reflection e gestione errori type-safe tramite `errors.AsType`. Questa guida tratta la composizione delle interfacce, le type assertion, i pattern di embedding e le domande che emergono nei colloqui tecnici. > **Miglioramento Interfacce Go 1.26** > > I tipi generici possono ora riferirsi a se stessi nella loro lista di parametri di tipo: `type Adder[A Adder[A]] interface { Add(A) A }`. Questo pattern di polimorfismo F-bounded abilita interfacce che vincolano i tipi di ritorno al tipo implementante stesso. ## Composizione delle Interfacce e Pattern di Embedding L'embedding delle interfacce combina più interfacce in un singolo contratto. Il risultato è una nuova interfaccia che richiede tutti i metodi dalle interfacce embedded. Questo pattern evita la duplicazione e crea confini di astrazione chiari. ```go // interfaces.go type Reader interface { Read(p []byte) (n int, err error) } type Writer interface { Write(p []byte) (n int, err error) } type Closer interface { Close() error } // Composed interface embedding three interfaces type ReadWriteCloser interface { Reader Writer Closer } ``` Qualsiasi tipo che implementa i metodi `Read`, `Write` e `Close` soddisfa automaticamente `ReadWriteCloser`. La [libreria standard Go](https://pkg.go.dev/io) utilizza questo pattern estensivamente nel package `io`. Una domanda comune nei colloqui chiede ai candidati di spiegare perché Go preferisce interfacce piccole e focalizzate. La risposta sta nella componibilità: `io.Reader` appare in centinaia di funzioni perché richiede esattamente un metodo. Interfacce più grandi creano accoppiamento più stretto e riducono il riutilizzo. ## Type Assertion: Sintassi e Sicurezza Le type assertion estraggono un tipo concreto da un valore di interfaccia. La forma a due valori previene i panic restituendo un booleano che indica il successo. ```go // assertions.go func processValue(v interface{}) { // Two-value assertion: safe, no panic if str, ok := v.(string); ok { fmt.Printf("String value: %s\n", str) return } // Type switch for multiple types switch val := v.(type) { case int: fmt.Printf("Integer: %d\n", val) case float64: fmt.Printf("Float: %.2f\n", val) case []byte: fmt.Printf("Bytes: %x\n", val) default: fmt.Printf("Unknown type: %T\n", val) } } ``` I type switch gestiscono elegantemente più tipi possibili. Ogni case lega `val` al tipo asserito all'interno del suo blocco, eliminando la necessità di assertion separate. > **Rischio di Panic** > > Le assertion a valore singolo come `str := v.(string)` causano panic se l'assertion fallisce. Il codice di produzione dovrebbe sempre usare la forma a due valori o un type switch. Gli intervistatori spesso chiedono delle performance delle type assertion. Il runtime esegue un singolo confronto di descrittori di tipo, rendendo le assertion economiche. Il costo aumenta con valori di interfaccia che wrappano puntatori a struct grandi a causa dell'indirezione, ma l'assertion stessa rimane O(1). ## Generics Autoreferenziali in Go 1.26 Go 1.26 ha introdotto parametri di tipo autoreferenziali, abilitando interfacce dove i metodi devono restituire il tipo implementante. Questo pattern, talvolta chiamato [polimorfismo F-bounded](https://en.wikipedia.org/wiki/Bounded_quantification#F-bounded_quantification), risolve una limitazione di lunga data. ```go // builder.go // Self-referential interface: methods return the same type type Builder[B Builder[B]] interface { WithName(name string) B WithAge(age int) B Build() string } type PersonBuilder struct { name string age int } func (p PersonBuilder) WithName(name string) PersonBuilder { p.name = name return p } func (p PersonBuilder) WithAge(age int) PersonBuilder { p.age = age return p } func (p PersonBuilder) Build() string { return fmt.Sprintf("%s, %d years old", p.name, p.age) } // Generic function using the self-referential constraint func configure[B Builder[B]](b B, name string, age int) string { return b.WithName(name).WithAge(age).Build() } ``` Il vincolo `Builder[B]` assicura che `WithName` e `WithAge` restituiscano `B`, non un `Builder` generico. Senza questo, il tipo di ritorno sarebbe l'interfaccia, perdendo le informazioni del tipo concreto e interrompendo il method chaining. I tipi matematici beneficiano di questo pattern. Un'interfaccia `Addable[A Addable[A]]` assicura che `Add(A) A` restituisca lo stesso tipo numerico, prevenendo il mix accidentale di valori `BigInt` e `Decimal`. ## errors.AsType: Unwrapping Errori Type-Safe Go 1.26 ha aggiunto [`errors.AsType`](https://pkg.go.dev/errors#AsType), sostituendo il pattern pointer-dance di `errors.As`. La versione generica restituisce l'errore unwrapped direttamente. ```go // errors_handling.go type ValidationError struct { Field string Message string } func (e *ValidationError) Error() string { return fmt.Sprintf("validation failed on %s: %s", e.Field, e.Message) } func handleError(err error) { // Go 1.26: generic type-safe unwrapping if valErr, ok := errors.AsType[*ValidationError](err); ok { log.Printf("Validation error on field %s\n", valErr.Field) return } // Before Go 1.26: pointer-based unwrapping // var valErr *ValidationError // if errors.As(err, &valErr) { ... } log.Printf("Unexpected error: %v\n", err) } ``` La nuova API elimina la dichiarazione di variabile separata e rende il tipo target esplicito nella chiamata di funzione. Le catene di errori vengono cercate allo stesso modo di prima; solo l'interfaccia è cambiata. ## Iteratori Reflection per l'Ispezione delle Interfacce Go 1.26 ha aggiunto metodi iteratori al package `reflect`. `Type.Methods()` e `Value.Methods()` restituiscono iteratori per iterare sui metodi, sostituendo i loop basati su indici. ```go // reflection.go import "reflect" func inspectInterface(v interface{}) { t := reflect.TypeOf(v) // Go 1.26: iterator-based method inspection fmt.Printf("Type %s methods:\n", t.Name()) for method := range t.Methods() { fmt.Printf(" %s: %s\n", method.Name, method.Type) } // For struct fields (also new in 1.26) if t.Kind() == reflect.Struct { for field := range t.Fields() { fmt.Printf(" Field: %s (%s)\n", field.Name, field.Type) } } } ``` Il pattern iteratore si allinea con la funzionalità range-over-function di Go 1.23. Il codice diventa più leggibile e non c'è penalità di performance: gli iteratori producono valori lazily. ## Soddisfacimento delle Interfacce a Compile Time Go verifica il soddisfacimento delle interfacce a compile time quando si assegna un valore concreto a una variabile di interfaccia. Verifiche esplicite usando assegnazioni con identificatore blank catturano gli errori presto nelle codebase grandi. ```go // compile_check.go type Storage interface { Save(key string, data []byte) error Load(key string) ([]byte, error) Delete(key string) error } type FileStorage struct { basePath string } // Compile-time check: fails if FileStorage misses a method var _ Storage = (*FileStorage)(nil) func (f *FileStorage) Save(key string, data []byte) error { path := filepath.Join(f.basePath, key) return os.WriteFile(path, data, 0644) } func (f *FileStorage) Load(key string) ([]byte, error) { path := filepath.Join(f.basePath, key) return os.ReadFile(path) } func (f *FileStorage) Delete(key string) error { return os.Remove(filepath.Join(f.basePath, key)) } ``` La riga `var _ Storage = (*FileStorage)(nil)` compila solo se `*FileStorage` soddisfa `Storage`. Questo pattern cattura i metodi mancanti immediatamente piuttosto che a runtime quando un valore viene assegnato. ## L'Interfaccia Vuota e i Vincoli di Tipo L'interfaccia vuota `interface{}` accetta qualsiasi valore, mentre `any` è il suo alias da Go 1.18. I vincoli generici forniscono type safety a compile time senza assertion a runtime. ```go // constraints.go import "golang.org/x/exp/constraints" // Generic function with numeric constraint func Sum[T constraints.Integer | constraints.Float](values []T) T { var total T for _, v := range values { total += v } return total } // Comparable constraint for map keys func Contains[K comparable, V any](m map[K]V, key K) bool { _, exists := m[key] return exists } ``` Preferire i generics a `interface{}` elimina le type assertion e cattura i mismatch di tipo a compile time. Il compromesso è una complessità aggiunta nelle signature delle funzioni, quindi i generics sono più adatti quando una funzione opera genuinamente su più tipi. ## Domande Tecniche da Colloquio sulle Interfacce Go Gli intervistatori testano la conoscenza delle interfacce a più livelli. Ecco le domande comuni con risposte concise. **D: Cosa succede quando si chiama un metodo su un valore di interfaccia nil rispetto a un valore concreto nil all'interno di un'interfaccia?** Un'interfaccia nil non ha tipo né valore; chiamare qualsiasi metodo causa panic. Un'interfaccia non-nil che contiene un puntatore nil ha un tipo; il metodo viene eseguito con un receiver nil. Questo comportamento permette pattern come `(*bytes.Buffer)(nil).String()` che restituisce una stringa vuota. **D: Come funziona il confronto tra interfacce?** Due valori di interfaccia sono uguali se hanno lo stesso tipo dinamico e valori dinamici uguali. Confrontare interfacce con tipi non comparabili (slice, map, funzioni) causa panic a runtime. **D: Quando un metodo dovrebbe usare un pointer receiver rispetto a un value receiver?** I pointer receiver permettono la mutazione ed evitano la copia di struct grandi. I value receiver sono sicuri per l'uso concorrente e funzionano sia con valori che con puntatori. Se qualsiasi metodo necessita di un pointer receiver, tutti i metodi su quel tipo dovrebbero usare pointer receiver per consistenza, poiché solo `*T` soddisfa un'interfaccia che richiede un metodo con pointer receiver. ```go // receiver_rules.go type Counter struct { count int } // Pointer receiver: modifies the struct func (c *Counter) Increment() { c.count++ } // Value receiver: read-only operation func (c Counter) Value() int { return c.count } type Incrementer interface { Increment() } func main() { var c Counter // var i Incrementer = c // Compile error: Counter lacks Increment var i Incrementer = &c // OK: *Counter has Increment i.Increment() } ``` **D: Spiega il principio "accetta interfacce, restituisci struct".** Le funzioni che accettano interfacce si disaccoppiano dalle implementazioni concrete, abilitando il testing con mock. Restituire tipi concreti dà ai chiamanti pieno accesso ai metodi del tipo senza type assertion. Restituire un'interfaccia nasconde le future aggiunte di metodi dietro una type assertion. Per ulteriore preparazione ai colloqui Go, consultare il modulo [Go concurrency questions](/technologies/go/interview-questions/concurrency-patterns) e il [modulo testing](/technologies/go/interview-questions/testing). > **Suggerimento per il Colloquio** > > Quando viene chiesto di progettare un'interfaccia, partire dal caso con un singolo metodo. Espandere solo quando più metodi vengono sempre chiamati insieme. Le interfacce `Stringer`, `Reader` e `Handler` della libreria standard definiscono ciascuna un metodo. ## Cosa Dovrebbero Sapere gli Sviluppatori Go Senior sulle Interfacce - La composizione delle interfacce tramite embedding crea contratti flessibili senza gerarchie di ereditarietà. Mantenere le interfacce piccole: da uno a tre metodi copre la maggior parte dei casi d'uso. - Type assertion e type switch estraggono tipi concreti in modo sicuro. Usare sempre la forma a due valori o uno switch nel codice di produzione per evitare panic. - I generics autoreferenziali di Go 1.26 abilitano pattern builder e tipi matematici dove i metodi devono restituire il tipo implementante. - `errors.AsType` semplifica l'unwrapping degli errori con un'API generica e type-safe. Utilizzarlo nel nuovo codice destinato a Go 1.26 o successivi. - Le verifiche di interfaccia a compile time con `var _ Interface = (*Type)(nil)` catturano i metodi mancanti prima che i test vengano eseguiti. - Preferire i generics a `interface{}` quando la type safety a compile time supera la complessità nelle signature. - La linea guida "accetta interfacce, restituisci struct" mantiene le API flessibili per i chiamanti esponendo la piena funzionalità. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/it/blog/go/go-interfaces-advanced-composition-type-assertions-2026