# Go: Basiskennis voor Java/Python-ontwikkelaars in 2026 > Leer Go snel door je Java- of Python-ervaring te benutten. Goroutines, channels, interfaces en essentiële patronen voor een vlotte overstap. - Published: 2026-01-19 - Updated: 2026-04-10 - Author: SharpSkill - Tags: go, golang, concurrency, goroutines, backend - Reading time: 12 min --- Go (of Golang) heeft zich gevestigd als de taal bij uitstek voor microservices, CLI-tools en gedistribueerde systemen. In 2009 ontwikkeld door Google, combineert het de eenvoud van Python met de prestaties van C. Voor ontwikkelaars afkomstig uit Java of Python verloopt de overstap naar Go verrassend soepel zodra de kernconcepten op hun plek vallen. > **Waarom Go in 2026?** > > Go vormt de basis van Docker, Kubernetes, Terraform en talloze cloud-infrastructuren. De snelle compilatie, native concurrency-ondersteuning en deployment als enkele binary maken het ideaal voor moderne backend-ontwikkeling. ## Go installeren en configureren Go installeren is eenvoudig en consistent op alle platformen. De `go`-tool beheert compilatie, afhankelijkheden en testing. ```bash # install.sh # Installation on macOS with Homebrew brew install go # Installation on Linux (Ubuntu/Debian) sudo apt update && sudo apt install golang-go # Verify installation go version # go version go1.22.0 linux/amd64 ``` De projectstructuur in Go volgt strikte maar eenvoudige conventies. Het `go.mod`-bestand definieert de module en de bijbehorende afhankelijkheden. ```bash # project-setup.sh # Create a new project mkdir my-project && cd my-project go mod init github.com/user/my-project # Generated structure: # my-project/ # ├── go.mod # Module manifest # └── main.go # Entry point # Essential commands go build # Compile the project go run main.go # Compile and execute go test ./... # Run all tests go fmt ./... # Format code automatically ``` ## Eerste Go-programma Hieronder staat een eenvoudig programma dat de basissyntax van Go illustreert. De vergelijking met Java en Python helpt de verschillen te visualiseren. ```go // main.go package main import "fmt" // Program entry point func main() { // Declaration with type inference message := "Hello, Go!" fmt.Println(message) // Explicit declaration var count int = 42 fmt.Printf("Count: %d\n", count) } ``` Wat meteen opvalt: geen puntkomma's, geen haakjes rond condities en type-inferentie met `:=`. Go geeft prioriteit aan bondigheid zonder leesbaarheid op te offeren. ## Variabelen en fundamentele types Go is statisch getypeerd maar biedt uitstekende type-inferentie. De basistypen dekken de meeste gebruikssituaties. ```go // types.go package main import "fmt" func main() { // Short declaration (inside functions only) name := "Alice" // string age := 30 // int height := 1.75 // float64 active := true // bool // Explicit declaration var score int = 100 var rate float64 = 3.14 // Multiple declaration var ( firstName string = "Bob" lastName string = "Smith" points int = 0 ) // Zero values (default values) var count int // 0 var text string // "" (empty string) var flag bool // false var ptr *int // nil fmt.Println(name, age, height, active) } ``` > **Zero Values in Go** > > Anders dan in Java of Python initialiseert Go variabelen automatisch naar hun "zero value": 0 voor getallen, "" voor strings, false voor bools, nil voor pointers en slices. ## Functies en meervoudige returnwaarden Go staat toe om meerdere waarden te retourneren, een eigenschap die uitgebreid wordt gebruikt voor foutafhandeling. ```go // functions.go package main import ( "errors" "fmt" ) // Simple function with typed parameters func add(a, b int) int { return a + b } // Multiple returns (idiomatic pattern for errors) func divide(a, b float64) (float64, error) { if b == 0 { return 0, errors.New("division by zero") } return a / b, nil } // Named returns func getUser(id int) (name string, age int, err error) { if id <= 0 { err = errors.New("invalid ID") return } name = "Alice" age = 30 return } // Variadic function func sum(numbers ...int) int { total := 0 for _, n := range numbers { total += n } return total } func main() { // Simple call result := add(5, 3) fmt.Println("5 + 3 =", result) // Explicit error handling quotient, err := divide(10, 3) if err != nil { fmt.Println("Error:", err) return } fmt.Printf("10 / 3 = %.2f\n", quotient) // Ignore a returned value with _ name, _, _ := getUser(1) fmt.Println("User:", name) // Variadic call total := sum(1, 2, 3, 4, 5) fmt.Println("Sum:", total) } ``` ## Structs en methoden Structs zijn de bouwstenen voor aangepaste types in Go. Methoden worden via receivers aan types gekoppeld. ```go // structs.go package main import "fmt" // Struct definition type User struct { ID int Username string Email string Active bool } // Constructor (convention: NewTypeName) func NewUser(id int, username, email string) *User { return &User{ ID: id, Username: username, Email: email, Active: true, } } // Method with value receiver (copy) func (u User) FullInfo() string { status := "inactive" if u.Active { status = "active" } return fmt.Sprintf("%s <%s> (%s)", u.Username, u.Email, status) } // Method with pointer receiver (modification possible) func (u *User) Deactivate() { u.Active = false } // Method with pointer receiver for modification func (u *User) UpdateEmail(newEmail string) { u.Email = newEmail } func main() { // Create with constructor user := NewUser(1, "alice", "alice@example.com") fmt.Println(user.FullInfo()) // Modify via method user.Deactivate() fmt.Println(user.FullInfo()) // Direct creation user2 := User{ ID: 2, Username: "bob", Email: "bob@example.com", } fmt.Println(user2.FullInfo()) } ``` > **Value vs Pointer Receiver** > > Gebruik een pointer receiver (`*User`) wanneer de methode de toestand wijzigt of wanneer het struct groot is. Gebruik een value receiver (`User`) voor read-only methoden op lichtgewicht structs. ## Interfaces: impliciet polymorfisme Go-interfaces worden impliciet geïmplementeerd. Een type voldoet aan een interface als het alle methoden implementeert, zonder expliciete declaratie. ```go // interfaces.go package main import ( "fmt" "math" ) // Interface definition type Shape interface { Area() float64 Perimeter() float64 } // Rectangle implements Shape implicitly type Rectangle struct { Width, Height float64 } func (r Rectangle) Area() float64 { return r.Width * r.Height } func (r Rectangle) Perimeter() float64 { return 2 * (r.Width + r.Height) } // Circle also implements Shape type Circle struct { Radius float64 } func (c Circle) Area() float64 { return math.Pi * c.Radius * c.Radius } func (c Circle) Perimeter() float64 { return 2 * math.Pi * c.Radius } // Function accepting the interface func PrintShapeInfo(s Shape) { fmt.Printf("Area: %.2f, Perimeter: %.2f\n", s.Area(), s.Perimeter()) } func main() { rect := Rectangle{Width: 10, Height: 5} circle := Circle{Radius: 7} // Polymorphism via interface PrintShapeInfo(rect) PrintShapeInfo(circle) // Slice of interfaces shapes := []Shape{rect, circle} for _, shape := range shapes { PrintShapeInfo(shape) } } ``` Deze aanpak verschilt fundamenteel van Java, waar `implements` verplicht is. In Go is conformiteit structureel, niet nominaal. ## Slices en maps: dynamische collecties Slices zijn dynamische weergaven over arrays en maps zijn native hash-tabellen. ```go // collections.go package main import "fmt" func main() { // Slice: dynamic array numbers := []int{1, 2, 3, 4, 5} // Append elements numbers = append(numbers, 6, 7) // Slicing (similar to Python) subset := numbers[1:4] // [2, 3, 4] fmt.Println("Subset:", subset) // Create slice with make scores := make([]int, 0, 10) // len=0, cap=10 scores = append(scores, 100, 95, 88) // Iteration with range for index, value := range numbers { fmt.Printf("numbers[%d] = %d\n", index, value) } // Map: hash table users := map[string]int{ "alice": 30, "bob": 25, } // Add/Update users["charlie"] = 35 // Check existence age, exists := users["alice"] if exists { fmt.Println("Alice's age:", age) } // Delete delete(users, "bob") // Map iteration for name, age := range users { fmt.Printf("%s is %d years old\n", name, age) } } ``` ## Idiomatische foutafhandeling Go kent geen exceptions. Fouten zijn waarden die expliciet worden geretourneerd, wat een rigoureuze afhandeling afdwingt. ```go // errors.go package main import ( "errors" "fmt" "os" ) // Sentinel error (for comparison) var ErrNotFound = errors.New("resource not found") var ErrInvalidInput = errors.New("invalid input") // Custom error with context type ValidationError struct { Field string Message string } func (e *ValidationError) Error() string { return fmt.Sprintf("validation failed on %s: %s", e.Field, e.Message) } // Function returning different error types func GetUser(id int) (string, error) { if id <= 0 { return "", &ValidationError{ Field: "id", Message: "must be positive", } } if id > 1000 { return "", ErrNotFound } return "Alice", nil } // Error wrapping (Go 1.13+) func ReadConfig(path string) ([]byte, error) { data, err := os.ReadFile(path) if err != nil { return nil, fmt.Errorf("reading config %s: %w", path, err) } return data, nil } func main() { // Basic pattern user, err := GetUser(-1) if err != nil { fmt.Println("Error:", err) // Type assertion for custom error var valErr *ValidationError if errors.As(err, &valErr) { fmt.Printf("Field: %s\n", valErr.Field) } // Comparison with sentinel error if errors.Is(err, ErrNotFound) { fmt.Println("User not found") } } else { fmt.Println("User:", user) } } ``` > **errors.Is en errors.As** > > Sinds Go 1.13 wordt `errors.Is()` gebruikt om te vergelijken met sentinel-fouten en `errors.As()` om een specifiek fouttype te extraheren uit een keten van gewrapte fouten. ## Goroutines: lichtgewicht concurrency Goroutines zijn lichtgewicht threads beheerd door de Go-runtime. Het starten van een goroutine kost slechts enkele KB geheugen. ```go // goroutines.go package main import ( "fmt" "sync" "time" ) func worker(id int, wg *sync.WaitGroup) { defer wg.Done() // Decrement counter when done fmt.Printf("Worker %d starting\n", id) time.Sleep(time.Second) fmt.Printf("Worker %d done\n", id) } func main() { var wg sync.WaitGroup // Launch 5 goroutines for i := 1; i <= 5; i++ { wg.Add(1) go worker(i, &wg) // 'go' prefix launches the goroutine } // Wait for all goroutines to complete wg.Wait() fmt.Println("All workers completed") } ``` De `sync.WaitGroup` maakt het mogelijk om te wachten tot meerdere goroutines zijn voltooid. Dit is het basispatroon voor parallellisme in Go. ## Channels: communicatie tussen goroutines Channels zijn getypeerde conduits voor communicatie tussen goroutines. Ze maken veilige synchronisatie en gegevensuitwisseling mogelijk. ```go // channels.go package main import ( "fmt" "time" ) func producer(ch chan<- int) { for i := 1; i <= 5; i++ { fmt.Println("Producing:", i) ch <- i // Send on channel time.Sleep(100 * time.Millisecond) } close(ch) // Close channel when done } func consumer(ch <-chan int, done chan<- bool) { for value := range ch { // Iterate until closed fmt.Println("Consuming:", value) } done <- true } func main() { ch := make(chan int) // Unbuffered channel done := make(chan bool) go producer(ch) go consumer(ch, done) <-done // Wait for consumer to finish fmt.Println("All done") } ``` ### Gebufferde channels en select ```go // channels_advanced.go package main import ( "fmt" "time" ) func main() { // Buffered channel (capacity 3) buffered := make(chan int, 3) buffered <- 1 buffered <- 2 buffered <- 3 // buffered <- 4 // Would block since buffer is full fmt.Println(<-buffered) // 1 // Select: channel multiplexing ch1 := make(chan string) ch2 := make(chan string) go func() { time.Sleep(100 * time.Millisecond) ch1 <- "from ch1" }() go func() { time.Sleep(200 * time.Millisecond) ch2 <- "from ch2" }() // Wait for first available message for i := 0; i < 2; i++ { select { case msg1 := <-ch1: fmt.Println("Received:", msg1) case msg2 := <-ch2: fmt.Println("Received:", msg2) case <-time.After(500 * time.Millisecond): fmt.Println("Timeout!") } } } ``` > **Go-filosofie: CSP** > > Go volgt het CSP-model (Communicating Sequential Processes): "Communiceer niet door geheugen te delen; deel geheugen door te communiceren." Channels voorkomen race conditions. ## Testen in Go Go bevat een minimalistisch maar effectief testframework. Testbestanden eindigen op `_test.go`. ```go // calculator.go package calculator func Add(a, b int) int { return a + b } func Divide(a, b int) (int, error) { if b == 0 { return 0, errors.New("division by zero") } return a / b, nil } ``` ```go // calculator_test.go package calculator import ( "testing" ) // Basic test func TestAdd(t *testing.T) { result := Add(2, 3) expected := 5 if result != expected { t.Errorf("Add(2, 3) = %d; want %d", result, expected) } } // Table-driven tests (recommended pattern) func TestAddTableDriven(t *testing.T) { tests := []struct { name string a, b int expected int }{ {"positive numbers", 2, 3, 5}, {"negative numbers", -2, -3, -5}, {"zero", 0, 0, 0}, {"mixed", -5, 10, 5}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := Add(tt.a, tt.b) if result != tt.expected { t.Errorf("Add(%d, %d) = %d; want %d", tt.a, tt.b, result, tt.expected) } }) } } // Error test func TestDivideByZero(t *testing.T) { _, err := Divide(10, 0) if err == nil { t.Error("Expected error for division by zero") } } // Benchmark func BenchmarkAdd(b *testing.B) { for i := 0; i < b.N; i++ { Add(100, 200) } } ``` Tests worden uitgevoerd met `go test ./...` en benchmarks met `go test -bench=.`. ## HTTP: minimalistische webserver Go blinkt uit in het maken van krachtige HTTP-servers met de standaardbibliotheek. ```go // server.go package main import ( "encoding/json" "log" "net/http" ) type User struct { ID int `json:"id"` Name string `json:"name"` } func main() { // Simple route http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("Hello, Go!")) }) // JSON route http.HandleFunc("/api/users", func(w http.ResponseWriter, r *http.Request) { users := []User{ {ID: 1, Name: "Alice"}, {ID: 2, Name: "Bob"}, } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(users) }) // Route with method http.HandleFunc("/api/user", func(w http.ResponseWriter, r *http.Request) { switch r.Method { case "GET": w.Write([]byte("Get user")) case "POST": w.Write([]byte("Create user")) default: http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) } }) log.Println("Server starting on :8080") log.Fatal(http.ListenAndServe(":8080", nil)) } ``` Deze server verwerkt duizenden gelijktijdige verbindingen dankzij goroutines. Elk verzoek wordt automatisch in een eigen goroutine afgehandeld. ## Conclusie Go biedt een pragmatische benadering van backend-ontwikkeling: eenvoudige syntax, snelle compilatie, native concurrency en uitstekende tooling. Voor Java- of Python-ontwikkelaars vereist de overstap het accepteren van enkele andere conventies (expliciete foutafhandeling, beperkte generics vóór Go 1.18), maar de voordelen qua prestaties en onderhoudbaarheid zijn direct merkbaar. ### Checklist om te beginnen - ✅ Go installeren via de officiële website of pakketbeheerder - ✅ De commando's `go build`, `go run`, `go test`, `go fmt` beheersen - ✅ Het verschil tussen slices en arrays begrijpen - ✅ Het `if err != nil`-patroon voor foutafhandeling toepassen - ✅ Goroutines en channels voor concurrency gebruiken - ✅ Table-driven tests schrijven met het `testing`-pakket Het Go-ecosysteem is volwassen, met populaire frameworks zoals Gin, Echo en Fiber voor webontwikkeling, en tools zoals Cobra voor CLI's. Met deze solide basis wordt het verkennen van geavanceerde onderwerpen zoals generics (Go 1.18+), het context-pakket en concurrency-patronen goed toegankelijk. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/nl/blog/go/go-basics-java-python-developers