# Geavanceerde Go Interfaces in 2026: Compositie, Type Assertions en Sollicitatievragen > Een uitgebreide gids over Go interfaces: compositie via embedding, veilige type assertions, zelfreferentiële generics in Go 1.26 en de meest voorkomende sollicitatievragen voor senior ontwikkelaars. - Published: 2026-09-10 - Updated: 2026-09-10 - Author: Anthony Fillion-Maillet - Tags: go, interfaces, type assertions, generics, interview - Reading time: 5 min --- Go interfaces definiëren gedrag zonder implementatie voor te schrijven, waardoor ze centraal staan bij het schrijven van flexibele, testbare code. Go 1.26 breidt deze kracht uit met zelfreferentiële generics, nieuwe reflection iterators en type-safe error handling via `errors.AsType`. Deze gids behandelt interface compositie, type assertions, embedding patterns en de vragen die in technische sollicitatiegesprekken voorkomen. > **Go 1.26 Interface Verbetering** > > Generieke types kunnen nu naar zichzelf verwijzen in hun type parameterlijst: `type Adder[A Adder[A]] interface { Add(A) A }`. Dit F-bounded polymorphism pattern maakt interfaces mogelijk die return types beperken tot het implementerende type zelf. ## Interface Compositie en Embedding Patterns Interface embedding combineert meerdere interfaces tot één contract. Het resultaat is een nieuwe interface die alle methodes van de embedded interfaces vereist. Dit pattern vermijdt duplicatie en creëert duidelijke abstractiegrenzen. ```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 } ``` Elk type dat de methodes `Read`, `Write` en `Close` implementeert, voldoet automatisch aan `ReadWriteCloser`. De [Go standaardbibliotheek](https://pkg.go.dev/io) gebruikt dit pattern uitgebreid in het `io` package. Een veelvoorkomende sollicitatievraag vraagt kandidaten te verklaren waarom Go de voorkeur geeft aan kleine, gefocuste interfaces. Het antwoord ligt in composabiliteit: `io.Reader` verschijnt in honderden functies omdat het precies één methode vereist. Grotere interfaces creëren strakkere koppeling en verminderen herbruikbaarheid. ## Type Assertions: Syntax en Veiligheid Type assertions extraheren een concreet type uit een interface waarde. De twee-waarde vorm voorkomt panics door een boolean terug te geven die succes aangeeft. ```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) } } ``` Type switches behandelen meerdere mogelijke types netjes. Elke case bindt `val` aan het geasserteerde type binnen zijn blok, waardoor afzonderlijke assertions overbodig worden. > **Panic Risico** > > Enkelwaarde assertions zoals `str := v.(string)` veroorzaken een panic als de assertion faalt. Productiecode moet altijd de twee-waarde vorm of een type switch gebruiken. Interviewers vragen vaak naar de performance van type assertions. De runtime voert een enkele vergelijking van type descriptors uit, waardoor assertions goedkoop zijn. De kosten stijgen bij interface waarden die pointers naar grote structs wrappen vanwege indirectie, maar de assertion zelf blijft O(1). ## Zelfreferentiële Generics in Go 1.26 Go 1.26 introduceerde zelfreferentiële type parameters, waardoor interfaces mogelijk worden waar methodes het implementerende type moeten retourneren. Dit pattern, soms [F-bounded polymorphism](https://en.wikipedia.org/wiki/Bounded_quantification#F-bounded_quantification) genoemd, lost een langdurige beperking op. ```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() } ``` De constraint `Builder[B]` zorgt ervoor dat `WithName` en `WithAge` `B` retourneren, niet een generieke `Builder`. Zonder dit zou het return type de interface zijn, waardoor de concrete type informatie verloren gaat en method chaining verbroken wordt. Wiskundige types profiteren van dit pattern. Een `Addable[A Addable[A]]` interface zorgt ervoor dat `Add(A) A` hetzelfde numerieke type retourneert, waardoor per ongeluk mengen van `BigInt` en `Decimal` waarden voorkomen wordt. ## errors.AsType: Type-Safe Error Unwrapping Go 1.26 voegde [`errors.AsType`](https://pkg.go.dev/errors#AsType) toe, ter vervanging van het pointer-dance pattern van `errors.As`. De generieke versie retourneert de unwrapped error direct. ```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) } ``` De nieuwe API elimineert de afzonderlijke variabele declaratie en maakt het doeltype expliciet in de functie aanroep. Error chains worden op dezelfde manier doorzocht als voorheen; alleen de interface is veranderd. ## Reflection Iterators voor Interface Inspectie Go 1.26 voegde iterator methodes toe aan het `reflect` package. `Type.Methods()` en `Value.Methods()` retourneren iterators voor het doorlopen van methodes, ter vervanging van index-gebaseerde loops. ```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) } } } ``` Het iterator pattern sluit aan bij de range-over-function functie van Go 1.23. Code wordt leesbaarder, en er is geen performance penalty: iterators produceren waarden lazy. ## Interface Voldoening bij Compile Time Go controleert interface voldoening bij compile time wanneer een concrete waarde wordt toegewezen aan een interface variabele. Expliciete controles met blank identifier toewijzingen vangen fouten vroeg in grote codebases. ```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)) } ``` De regel `var _ Storage = (*FileStorage)(nil)` compileert alleen als `*FileStorage` voldoet aan `Storage`. Dit pattern vangt ontbrekende methodes onmiddellijk op in plaats van tijdens runtime wanneer een waarde wordt toegewezen. ## De Lege Interface en Type Constraints De lege interface `interface{}` accepteert elke waarde, terwijl `any` de alias is sinds Go 1.18. Generieke constraints bieden compile time type safety zonder runtime assertions. ```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 } ``` De voorkeur geven aan generics boven `interface{}` elimineert type assertions en vangt type mismatches bij compile time. De afweging is toegevoegde complexiteit in functie signatures, dus generics passen het beste wanneer een functie werkelijk op meerdere types opereert. ## Technische Sollicitatievragen over Go Interfaces Interviewers testen interface kennis op meerdere niveaus. Hier zijn veelvoorkomende vragen met beknopte antwoorden. **V: Wat gebeurt er wanneer een methode wordt aangeroepen op een nil interface waarde versus een nil concrete waarde binnen een interface?** Een nil interface heeft geen type en geen waarde; het aanroepen van een methode veroorzaakt een panic. Een non-nil interface die een nil pointer bevat heeft wel een type; de methode wordt uitgevoerd met een nil receiver. Dit gedrag maakt patterns mogelijk zoals `(*bytes.Buffer)(nil).String()` die een lege string retourneert. **V: Hoe werkt interface vergelijking?** Twee interface waarden zijn gelijk als ze hetzelfde dynamische type en gelijke dynamische waarden hebben. Het vergelijken van interfaces met niet-vergelijkbare types (slices, maps, functies) veroorzaakt een runtime panic. **V: Wanneer moet een methode een pointer receiver versus een value receiver gebruiken?** Pointer receivers maken mutatie mogelijk en vermijden het kopiëren van grote structs. Value receivers zijn veilig voor concurrent gebruik en werken met zowel waarden als pointers. Als een methode een pointer receiver nodig heeft, moeten alle methodes op dat type pointer receivers gebruiken voor consistentie, aangezien alleen `*T` voldoet aan een interface die een pointer-receiver methode vereist. ```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() } ``` **V: Leg het principe "accepteer interfaces, retourneer structs" uit.** Functies die interfaces accepteren ontkoppelen van concrete implementaties, waardoor testen met mocks mogelijk wordt. Het retourneren van concrete types geeft aanroepers volledige toegang tot de methodes van het type zonder type assertions. Het retourneren van een interface verbergt toekomstige methode toevoegingen achter een type assertion. Voor meer Go sollicitatievoorbereidingen, zie de [Go concurrency questions](/technologies/go/interview-questions/concurrency-patterns) module en de [testing module](/technologies/go/interview-questions/testing). > **Sollicitatietip** > > Wanneer gevraagd wordt een interface te ontwerpen, begin met het enkele-methode geval. Breid alleen uit wanneer meerdere methodes altijd samen worden aangeroepen. De `Stringer`, `Reader` en `Handler` interfaces van de standaardbibliotheek definiëren elk één methode. ## Wat Senior Go Ontwikkelaars Moeten Weten over Interfaces - Interface compositie via embedding creëert flexibele contracten zonder overervingshiërarchieën. Houd interfaces klein: één tot drie methodes dekt de meeste use cases. - Type assertions en type switches extraheren concrete types veilig. Gebruik altijd de twee-waarde vorm of een switch in productiecode om panics te voorkomen. - Go 1.26's zelfreferentiële generics maken builder patterns en wiskundige types mogelijk waar methodes het implementerende type moeten retourneren. - `errors.AsType` vereenvoudigt error unwrapping met een generieke, type-safe API. Gebruik het in nieuwe code gericht op Go 1.26 of later. - Compile time interface controles met `var _ Interface = (*Type)(nil)` vangen ontbrekende methodes voordat tests draaien. - Geef de voorkeur aan generics boven `interface{}` wanneer type safety bij compile time opweegt tegen de complexiteit in signatures. - De richtlijn "accepteer interfaces, retourneer structs" houdt API's flexibel voor aanroepers terwijl volledige functionaliteit wordt blootgesteld. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/nl/blog/go/go-interfaces-advanced-composition-type-assertions-2026