Go hat keine Klassen. Stattdessen gibt es Structs (Datenverbunde) mit Methoden.
package main
import "fmt"
type Person struct {
Name string
Alter int
}
// Methode mit Wert-Empfänger: arbeitet auf einer Kopie
func (p Person) Vorstellen() string {
return fmt.Sprintf("%s (%d)", p.Name, p.Alter)
}
// Methode mit Zeiger-Empfänger: kann das Original ändern
func (p *Person) Geburtstag() {
p.Alter++
}
func main() {
mia := Person{Name: "Mia", Alter: 17}
tom := Person{"Tom", 19}
var leer Person // Nullwerte: "" und 0
mia.Geburtstag()
fmt.Println(mia.Vorstellen(), tom, leer)
fmt.Printf("%+v\n", mia)
p := &tom // Zeiger auf Person
p.Alter = 30 // Go dereferenziert automatisch
fmt.Println(tom.Alter, (*p).Alter)
n := new(Person) // Zeiger auf Nullwert
n.Name = "Neu"
fmt.Println(*n)
}Ausgabe
Mia (18) {Tom 19} { 0}
{Name:Mia Alter:18}
30 30
{Neu 0}Zeiger
Ein Zeiger (*T) enthält die Adresse einer Variable. &x liefert die Adresse, *p den Wert dahinter. Anders als in C gibt es keine Zeigerarithmetik, und der Garbage Collector verwaltet den Speicher.
package main
import "fmt"
func falsch(x int) { x = 99 }
func richtig(x *int) { *x = 99 }
type Zaehler struct{ n int }
func (z Zaehler) FalschErhoehen() { z.n++ } // Kopie: wirkungslos
func (z *Zaehler) Erhoehen() { z.n++ } // Original
func main() {
n := 1
falsch(n)
fmt.Println(n)
richtig(&n)
fmt.Println(n)
z := Zaehler{}
z.FalschErhoehen()
z.Erhoehen()
z.Erhoehen()
fmt.Println(z.n)
var p *int
fmt.Println(p == nil)
// fmt.Println(*p) // Panic: nil pointer dereference
}Ausgabe
1 99 2 true
Wert- oder Zeiger-Empfänger? Nimm *T, wenn die Methode den Zustand ändert oder das Struct groß ist. Mische beides nicht für denselben Typ.
Konstruktoren und Einbettung
Go hat keine Konstruktoren. Üblich sind Funktionen mit dem Namen NewXyz:
package main
import (
"errors"
"fmt"
)
type Konto struct {
inhaber string // klein geschrieben = nur im Paket sichtbar
stand float64
}
func NewKonto(inhaber string, start float64) (*Konto, error) {
if start < 0 {
return nil, errors.New("Startwert darf nicht negativ sein")
}
return &Konto{inhaber: inhaber, stand: start}, nil
}
func (k *Konto) Einzahlen(b float64) { k.stand += b }
func (k *Konto) Stand() float64 { return k.stand }
// Einbettung statt Vererbung
type Tier struct{ Name string }
func (t Tier) Vorstellen() string { return "Ich bin " + t.Name }
type Hund struct {
Tier // eingebettet: Felder und Methoden werden "hochgereicht"
Rasse string
}
func main() {
k, err := NewKonto("Mia", 100)
if err != nil {
fmt.Println(err)
return
}
k.Einzahlen(50)
fmt.Println(k.Stand())
_, err = NewKonto("X", -1)
fmt.Println(err)
h := Hund{Tier: Tier{Name: "Rex"}, Rasse: "Schäfer"}
fmt.Println(h.Name, h.Vorstellen(), h.Rasse)
}Ausgabe
150 Startwert darf nicht negativ sein Rex Ich bin Rex Schäfer
Sichtbarkeit regelt die Groß-/Kleinschreibung des ersten Buchstabens: Name ist exportiert (von anderen Paketen nutzbar), name nicht.
Struct-Tags, Vergleich und Kopie
package main
import (
"encoding/json"
"fmt"
)
type Artikel struct {
Name string `json:"name"`
Preis float64 `json:"preis"`
Notiz string `json:"notiz,omitempty"`
}
func main() {
a := Artikel{"Stift", 1.5, ""}
b := a // Kopie
b.Preis = 9
fmt.Println(a == Artikel{"Stift", 1.5, ""}, a.Preis, b.Preis) // Structs sind vergleichbar
daten, _ := json.Marshal(a)
fmt.Println(string(daten))
var c Artikel
json.Unmarshal([]byte(`{"name":"Heft","preis":2.5}`), &c)
fmt.Printf("%+v\n", c)
}Ausgabe
true 1.5 9
{"name":"Stift","preis":1.5}
{Name:Heft Preis:2.5 Notiz:}Methoden auf anderen Typen
Methoden kannst du auf jedem eigenen Typ definieren, nicht nur auf Structs:
package main
import (
"fmt"
"strings"
)
type Celsius float64
func (c Celsius) Fahrenheit() float64 { return float64(c)*9/5 + 32 }
type Woerter []string
func (w Woerter) Laengste() string {
l := ""
for _, x := range w {
if len(x) > len(l) {
l = x
}
}
return l
}
func (w Woerter) String() string { return strings.Join(w, "+") } // fmt nutzt String() automatisch
func main() {
fmt.Println(Celsius(100).Fahrenheit())
w := Woerter{"Go", "Gopher", "Sprache"}
fmt.Println(w.Laengste(), w)
}Ausgabe
212 Sprache Go+Gopher+Sprache
Merke
type Name struct { ... }bündelt Daten; Methoden stehen außerhalb:func (p Person) Methode()- Zeiger-Empfänger
(p *Person)ändern das Original, Wert-Empfänger arbeiten auf einer Kopie &xAdresse,*pWert; keine Zeigerarithmetik; automatisches Dereferenzieren bei Feldzugriff- Konstruktoren sind
NewXyz-Funktionen; Einbettung ersetzt Vererbung - Großbuchstabe = exportiert; Struct-Tags steuern z. B. JSON
Aufgabe
Modelliere einen Warenkorb mit Artikeln (Struct), Methoden Hinzufuegen und Summe und lass ihn als JSON ausgeben.