Das Error-Interface
Teil des Abschnitts Objektorientierte Programmierung der GO-Journey von Coddy. Lektion 52 von 107.
In Go werden Fehler über ein integriertes Interface namens error behandelt. Dieses Interface ist bemerkenswert einfach. Es erfordert nur eine Methode:
type error interface {
Error() string
}Jeder Typ, der eine Error()-Methode implementiert, die eine Zeichenkette zurückgibt, erfüllt automatisch das error-Interface. Das ist Go's Interfacesystem in Aktion: Keine explizite Deklaration erforderlich.
Die häufigste Methode zum Erstellen von Fehlern ist die Verwendung des Pakets errors:
import "errors"
func Divide(a, b int) (int, error) {
if b == 0 {
return 0, errors.New("cannot divide by zero")
}
return a / b, nil
}Go-Funktionen geben typischerweise einen Fehler als ihren letzten Rückgabewert zurück. Wenn kein Fehler auftritt, gibst du nil zurück. Der Aufrufer prüft dann, ob der Fehler nicht nil ist, bevor er fortfährt:
result, err := Divide(10, 0)
if err != nil {
fmt.Println("Error:", err.Error())
return
}
fmt.Println("Result:", result)Du kannst auch fmt.Errorf verwenden, um formatierte Fehlermeldungen zu erstellen:
func GetUser(id int) (string, error) {
if id <= 0 {
return "", fmt.Errorf("invalid user id: %d", id)
}
return "Alice", nil
}Dieses Muster, Fehler zurückzugeben, anstatt Ausnahmen auszulösen, ist grundlegend für Go. Es macht die Fehlerbehandlung explizit und zwingt dich dazu, zu berücksichtigen, was passiert, wenn etwas schiefgeht: ein wesentlicher Aspekt beim Schreiben robusten objektorientierten Codes.
Aufgabe
EinfachErstellen wir ein Bankkontosystem, das Gos Muster zur Fehlerbehandlung demonstriert. Du erstellst ein einfaches Konto, das Operationen validiert und aussagekräftige Fehler zurückgibt, wenn etwas schiefgeht.
Du organisierst deinen Code über zwei Dateien:
account.go: Erstelle eineBankAccount-Struktur mit einem exportierten FeldOwner(string) und einem nicht exportierten Feldbalance(float64). Implementiere Folgendes:NewBankAccount(owner string, initialDeposit float64) (*BankAccount, error)– einen Konstruktor, der einen Fehler mit der Meldung"initial deposit must be positive"zurückgibt, wenn die Ersteinzahlung null oder negativ ist. Erstelle und gib andernfalls das Konto zurück.Deposit(amount float64) error– fügt dem Kontostand Geld hinzu. Gibt einen Fehler mit der Meldung"deposit amount must be positive"zurück, wenn der Betrag null oder negativ ist.Withdraw(amount float64) error– entfernt Geld vom Kontostand. Gibt einen Fehler mit der Meldung"withdrawal amount must be positive"zurück, wenn der Betrag null oder negativ ist, oder"insufficient funds", wenn der Kontostand geringer als der Abhebungsbetrag ist.Balance() float64– gibt den aktuellen Kontostand zurück
main.go: Lies Kontodetails und Transaktionsbeträge aus der Eingabe ein. Versuche, ein Konto zu erstellen und Operationen auszuführen. Gib nach jeder Operation entweder die Erfolgsmeldung oder die Fehlermeldung aus.
Die folgenden Eingaben werden bereitgestellt:
- Zeile 1: Name des Kontoinhabers
- Zeile 2: Betrag der Ersteinzahlung
- Zeile 3: Einzahlungsbetrag
- Zeile 4: Abhebungsbetrag
Gib für jede Operation das Ergebnis aus:
- Nach der Kontoerstellung:
Account created for [Owner] with balance: $[balance]oderError: [error message] - Nach der Einzahlung:
Deposited successfully. New balance: $[balance]oderError: [error message] - Nach der Abhebung:
Withdrew successfully. New balance: $[balance]oderError: [error message]
Formatiere alle Kontostandwerte mit zwei Dezimalstellen. Wenn die Kontoerstellung fehlschlägt, überspringe die verbleibenden Operationen.
Wenn beispielsweise Alice, 100, 50 und 200 angegeben werden, sollte deine Ausgabe wie folgt aussehen:
Account created for Alice with balance: $100.00
Deposited successfully. New balance: $150.00
Error: insufficient fundsUnd wenn Bob, -50, 25 und 10 angegeben werden, sollte deine Ausgabe wie folgt aussehen:
Error: initial deposit must be positiveProbier es selbst
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Namen des Inhabers lesen
ownerName, _ := reader.ReadString('\n')
ownerName = strings.TrimSpace(ownerName)
// Read initial deposit
initialDepositStr, _ := reader.ReadString('\n')
initialDeposit, _ := strconv.ParseFloat(strings.TrimSpace(initialDepositStr), 64)
// Read deposit amount
depositStr, _ := reader.ReadString('\n')
depositAmount, _ := strconv.ParseFloat(strings.TrimSpace(depositStr), 64)
// Read withdrawal amount
withdrawStr, _ := reader.ReadString('\n')
withdrawAmount, _ := strconv.ParseFloat(strings.TrimSpace(withdrawStr), 64)
// TODO: Erstelle ein neues Bankkonto mit NewBankAccount
// Wenn ein Fehler auftritt, drucke "Error: [error message]" und kehre zurück
// Bei Erfolg drucke "Account created for [Owner] with balance: $[balance]"
// TODO: Versuche, den Einzahlungsbetrag einzuzahlen
// Wenn ein Fehler auftritt, drucke "Error: [error message]"
// If successful, print "Deposited successfully. New balance: $[balance]"
// TODO: Versuche, den Abhebungsbetrag abzuheben
// Wenn ein Fehler auftritt, drucke "Error: [error message]"
// If successful, print "Withdrew successfully. New balance: $[balance]"
// Denke daran, Kontostandswerte mit zwei Dezimalstellen mit fmt.Printf("%.2f", balance) zu formatieren
_ = ownerName
_ = initialDeposit
_ = depositAmount
_ = withdrawAmount
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP in Go
Externe DateienGo-Workspace & ModulePackages & ImportsExportierte vs. nicht exportierte NamenEinführung in OOP mit GoStructs als KlassenMethoden für Structs definierenPointer- vs. Value-ReceiverStruct-InitialisierungKonstruktorfunktionenRückblick – Einfacher Taschenrechner4Schnittstellen
Einführung in SchnittstellenImplizite ImplementierungSchnittstelle als VertragLeere Schnittstelle (any)TypzusicherungTypwechselZusammensetzung von SchnittstellenStringer- und Error-SchnittstellenRückblick – Formenrechner7Kapselung
Exportierte vs. nicht exportierte FelderKapselung auf PaketebeneGetter- und Setter-MethodenInformationsverbergung in GoRückblick – Studierendendatensätze10Generics (Go 1.18+)
Einführung in GenericsTypparameterTypbeschränkungenGenerische StrukturenWorkaround für generische MethodenZusammenfassung – Generische Sammlung2Typen & Structs im Detail
Grundlegende & zusammengesetzte TypenBenutzerdefinierte TypdefinitionenStruct-TagsAnonyme StructsVerschachtelte StructsNullwerte & StandardwerteRückblick – Kontaktbuch5Komposition statt Vererbung
Warum Go keine Vererbung hatGrundlagen der Struct-EinbettungMethoden-PromotionMehrere Structs einbettenEinbettung vs. AggregationVerbergen eingebetteter MethodenRückblick – Mitarbeiterhierarchie8Fehlerbehandlung & OOP
Das Error-InterfaceBenutzerdefinierte FehlertypenError-Wrapping (fmt.Errorf)Sentinel-Fehlererrors.Is() und errors.As()Panic, Defer und RecoverRückblick – Dateiparser3Zeiger & Speicher
Grundlagen von Zeigern in GoZeiger auf StructsÜbergabe per Wert vs. ReferenzDie Funktion new()Garbage Collection in GoRückblick – Verkettete Liste erstellen6Polymorphismus in Go
Polymorphismus über InterfacesDuck-Typing in GoRegeln zur Interface-ErfüllungPolymorphe SammlungenDependency InjectionZusammenfassung – Zahlungsprozessor9Konkurrenz & OOP
Grundlagen der GoroutinesChannels & KommunikationGepufferte vs. ungepufferte ChannelsSelect-Anweisungsync.Mutex & sync.RWMutexsync.WaitGroupThread-sicheres Struct-DesignRückblick – Worker PoolÜbe selbstständig: Online-Go-Compiler