Przejdź do głównej treści
eLearner.app
Moduł 5 · Lekcja 2 z 522/50 w kursie~14 min
Lekcje modułu (2/5)

Metody: odbiorca wartościowy vs wskaźnikowy

Metoda w Go to funkcja z odbiornikiem: specjalnym parametr zadeklarowany pomiędzy func a nazwą metody, która wiąże metodę na typ. Nie ma klas: można zdefiniować metody dowolny nazwany typ zadeklarowany w bieżącym pakiecie.

Składnia

Go
type Person struct {
    Name string
}

func (p Person) Greet() string {
    return "ciao " + p.Name
}

p := Person{Name: "Ada"}
fmt.Println(p.Greet())   // "ciao Ada"

Odbiornik p Person czyni Greet metodą Person. Wewnątrz metody, p jest zmienną jak każda inna.

Odbiorca wartości a odbiorca wskaźnika

Go
// VALUE receiver: operates on a COPY of the value
func (p Person) NameUpper() string {
    return strings.ToUpper(p.Name)
}

// POINTER receiver: operates on the original value, can mutate it
func (p *Person) Rename(n string) {
    p.Name = n
}

Za pomocą odbiornika wskaźnikowego możesz modyfikować odbiornik i wprowadzać zmiany są widoczne poza metodą. Z odbiorcą wartości nie możesz – zrobiłbyś to modyfikuj tylko kopię lokalną.

Go
p := Person{Name: "Ada"}
p.Rename("Grace")
fmt.Println(p.Name)   // "Grace" — change is visible

Kiedy używać odbiornika wskaźnika

Praktyczne zasady, w kolejności ważności:

  1. Musisz zmodyfikować odbiornik → odbiornik wskaźnika. Obowiązkowy.
  2. Struktura jest duża (w środku dziesiątki pól, tablic) → wskaźnik odbiorcy, aby uniknąć kosztownych kopii.
  3. Spójność: jeśli CO NAJMNIEJ jedna metoda danego typu ma odbiornik wskaźnika, użyj odbiornik wskazówek także dla innych. Bardzo ważna konwencja dla interfejsy (Moduł 6).
  4. Małe, niezmienne typy „wartościowe” (time.Time, liczby zespolone, small structs) → odbiornik wartości, bardziej czytelny.

Metody dla typów innych niż strukturalne

Możesz zdefiniować metody dla dowolnego typu NAMED zadeklarowanego w pakiecie:

Go
type Celsius float64

func (c Celsius) ToFahrenheit() float64 {
    return float64(c)*9/5 + 32
}

t := Celsius(20)
fmt.Println(t.ToFahrenheit())  // 68

Nie możesz ich definiować na typach z innych pakietów (np. int, string): musisz najpierw utwórz lokalny nazwany typ.

Spróbuj

Ćwiczenie#go.m5.l2.e1
Próby: 0Ładowanie...

Dodaj metodę łańcuchową Greet() (odbiorca wartości) do osoby, która zwraca „ciao <Nazwa>”.

Ładowanie edytora...
Pokaż wskazówkę

Odbiorca w nawiasie PRZED nazwą metody.

Rozwiązanie dostępne po 3 próbach

Ćwiczenie#go.m5.l2.e2
Próby: 0Ładowanie...

Dodaj metodę Rename(n string) z odbiornikiem POINTER, który zmienia p.Name.

Ładowanie edytora...
Pokaż wskazówkę

Odbiornik wskaźnika: `(p *Person)`.

Rozwiązanie dostępne po 3 próbach

Quiz#go.m5.l2.e3
Gotowe

Kiedy należy używać odbiornika wskaźnika (p *T)?

Go
func (t ?T) M() { ... }
Opcje odpowiedzi

Podsumowanie

  • Metoda = funkcja z odbiornikiem: func (r T) Name(...) ... { ... }.
  • Odbiorca wartości: operuje na kopii; Odbiornik wskaźnika: działa na oryginale.
  • Go automatycznie dokonuje konwersji T*T w miejscu połączenia.
  • Odbiornik wskaźnika, gdy: mutacja + duża struktura + spójność z innymi metodami tego typu.
  • Nigdy nie mieszaj odbiorników wartości i wskaźników tego samego typu.
  • Metody korzystają z typów NAMED w bieżącym pakiecie (w tym niestrukturalnych).