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.

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.
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.
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 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.
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.
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 genoemd, lost een langdurige beperking op.
// 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 toe, ter vervanging van het pointer-dance pattern van errors.As. De generieke versie retourneert de unwrapped error direct.
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.
Klaar om je Go gesprekken te halen?
Oefen met onze interactieve simulatoren, flashcards en technische tests.
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.
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.
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.
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.
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 module en de testing module.
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.AsTypevereenvoudigt 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.
Begin met oefenen!
Test je kennis met onze gespreksimulatoren en technische tests.
Zie jij de bug in Go?
Een echt codefragment, een verborgen bug, één poging per dag. Zonder account uit te proberen.

Geschreven door
Anthony Fillion-MailletOprichter van SharpSkill
Al meer dan 10 jaar fullstack-ontwikkelaar. Hij leidt SharpSkill en staat in voor alles wat hier verschijnt.
Bijgewerkt op 10 september 2026
Tags
Delen
Gerelateerde artikelen

Go Foutafhandeling in 2026: Patronen, Wrapping en Technische Interviewvragen
Uitgebreide gids over Go error handling: sentinel errors, error wrapping met fmt.Errorf, errors.Is/As en best practices voor technische interviews.

Go Design Patterns: Essentiële patronen en interviewvragen voor Go-ontwikkelaars
De zes belangrijkste Go design patterns met productieklare code: Functional Options, Strategy, Factory, Observer, Middleware en Struct Embedding. Inclusief veelgestelde interviewvragen.

Go Context Package in 2026: Cancellation, Timeouts en Interviewvragen
Uitgebreide handleiding voor het Go context package voor cancellation, deadlines en request-scoped values. Praktische voorbeelden en veelgestelde interviewvragen.