0Pricing
Go Academy · Lekcja

Definiowanie i implementowanie interfejsów

Niejawna zgodność i pusty interfejs

Definiowanie i implementowanie interfejsów to bezpłatna lekcja Go Academy na CoddyKit. To lekcja 1 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Go Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Go Academy zawiera 4 lekcji w sumie.

Czym jest interfejs?

Interfejs w Go to zbiór sygnatur metod. Każdy typ, który implementuje wszystkie te metody, niejawnie spełnia interfejs — nie jest potrzebne słowo kluczowe implements. Nazywa się to typowaniem strukturalnym lub typowaniem kaczym.

Deklarowanie interfejsu

Zdefiniuj interfejs za pomocą słowa kluczowego type:

package main
import "fmt"

type Shape interface {
    Area() float64
    Perimeter() float64
}

type Circle struct{ Radius float64 }
func (c Circle) Area() float64      { return 3.14159 * c.Radius * c.Radius }
func (c Circle) Perimeter() float64 { return 2 * 3.14159 * c.Radius }

func main() {
    var s Shape = Circle{5}
    fmt.Printf("Area: %.2f\n", s.Area())
}

Niejawne spełnianie interfejsu

Nie jest potrzebna żadna deklaracja intencji. Jeśli typ ma właściwe metody, automatycznie spełnia interfejs:

package main
import "fmt"

type Greeter interface{ Greet() string }

type English struct{}
func (e English) Greet() string { return "Hello" }

type Spanish struct{}
func (s Spanish) Greet() string { return "Hola" }

func say(g Greeter) { fmt.Println(g.Greet()) }

func main() {
    say(English{})
    say(Spanish{})
}

Pusty interfejs

interface{} (lub any w Go 1.18+) jest spełniany przez każdy typ. Używa się go, gdy typ jest nieznany w czasie kompilacji:

package main
import "fmt"

func printAny(v any) { fmt.Printf("%T: %v\n", v, v) }

func main() {
    printAny(42)
    printAny("hello")
    printAny([]int{1,2,3})
}

Wartości interfejsów

Wartość interfejsu przechowuje dwie rzeczy: konkretny typ i konkretną wartość. Pusty interfejs ma oba te elementy ustawione na nil:

package main
import "fmt"

type Animal interface{ Sound() string }
type Dog struct{}
func (d Dog) Sound() string { return "woof" }

func main() {
    var a Animal
    fmt.Println(a == nil) // true
    a = Dog{}
    fmt.Println(a == nil) // false
    fmt.Println(a.Sound())
}

Spełnianie wielu interfejsów

Typ może jednocześnie spełniać wiele interfejsów:

package main
import "fmt"

type Reader interface{ Read() string }
type Writer interface{ Write(string) }
type ReadWriter interface{ Reader; Writer }

type File struct{ content string }
func (f *File) Read() string       { return f.content }
func (f *File) Write(s string)     { f.content = s }

func main() {
    var rw ReadWriter = &File{}
    rw.Write("hello")
    fmt.Println(rw.Read())
}

Przypisywanie interfejsów

Szerszy interfejs może przechowywać węższą wartość konkretnego typu. Przypisywanie między typami interfejsów wymaga, aby konkretny typ spełniał oba interfejsy:

package main
import "fmt"

type Stringer interface{ String() string }

type Point struct{ X, Y int }
func (p Point) String() string { return fmt.Sprintf("(%d,%d)", p.X, p.Y) }

func main() {
    var s Stringer = Point{3, 4}
    fmt.Println(s.String())
}

Pułapka pustego interfejsu

Interfejs, który nie jest nil, a przechowuje konkretną wartość nil, NIE jest równy nil:

package main
import "fmt"

type MyErr struct{}
func (e *MyErr) Error() string { return "oops" }

func bad() error {
    var e *MyErr // nil pointer
    return e     // returns non-nil interface!
}

func main() {
    err := bad()
    fmt.Println(err == nil) // false — common gotcha
}

Interfejsy ułatwiają testowanie

Zdefiniowanie zależności jako interfejsów umożliwia zastępowanie rzeczywistych implementacji atrapami w testach:

package main
import "fmt"

type EmailSender interface{ Send(to, body string) error }

type MockSender struct{ Sent []string }
func (m *MockSender) Send(to, body string) error {
    m.Sent = append(m.Sent, to)
    return nil
}

func notify(s EmailSender, addr string) { s.Send(addr, "hello") }

func main() {
    mock := &MockSender{}
    notify(mock, "a@b.com")
    fmt.Println(mock.Sent) // [a@b.com]
}

Przyjmuj interfejsy, zwracaj struktury

Najlepsza praktyka w Go: parametry funkcji powinny być interfejsami (dla elastyczności), a typy zwracane powinny być konkretne (aby wywołujący wiedzieli, co otrzymują):

package main

type Logger interface{ Log(string) }

type FileLogger struct{}
func (f *FileLogger) Log(s string) {}

func NewFileLogger() *FileLogger { // returns concrete
    return &FileLogger{}
}

func process(l Logger) {} // accepts interface

func main() { process(NewFileLogger()) }

Utrzymuj małe interfejsy

Biblioteka standardowa Go preferuje niewielkie interfejsy: io.Reader ma jedną metodę, podobnie jak io.Writer. Małe interfejsy można łatwiej łączyć i zastępować atrapami.

Szybkie sprawdzenie

Czy typ musi zadeklarować, że implementuje interfejs?

Podsumowanie: interfejsy

Najważniejsze informacje:

  • Interfejsy definiują zachowanie, a nie dane
  • Spełnianie interfejsu jest niejawne — nie ma słowa kluczowego implements
  • Pusty interfejs (any) akceptuje każdy typ
  • Uważaj na pułapkę pustego interfejsu o wartości nil
  • Utrzymuj interfejsy małe i skoncentrowane

Polecenie ćwiczeniowe

Zdefiniuj interfejs Storage z metodami Save(key, value string) error i Load(key string) (string, error). Zaimplementuj go za pomocą MemStorage opartego na mapie i przetestuj.

Często zadawane pytania

Czy lekcja „Definiowanie i implementowanie interfejsów” jest bezpłatna?

Tak — pełny tekst „Definiowanie i implementowanie interfejsów” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Go Academy, przejdź na CoddyKit PRO. Kurs Go Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „Definiowanie i implementowanie interfejsów”?

Niejawna zgodność i pusty interfejs Ćwiczysz Go Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć Go Academy?

Nie wymagamy żadnego doświadczenia. Go Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 1 z 4.

Ile czasu zajmuje lekcja „Definiowanie i implementowanie interfejsów”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji Go Academy?

Tak. Każda lekcja Go Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Definiowanie i implementowanie interfejsów
  2. Podstawowe interfejsy biblioteki standardowej
  3. Asercje typów i przełączniki typów
  4. Kompozycja interfejsów i dobre praktyki
← Powrót do Go Academy