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
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
// 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ą.
p := Person{Name: "Ada"}
p.Rename("Grace")
fmt.Println(p.Name) // "Grace" — change is visibleKiedy używać odbiornika wskaźnika
Praktyczne zasady, w kolejności ważności:
- Musisz zmodyfikować odbiornik → odbiornik wskaźnika. Obowiązkowy.
- Struktura jest duża (w środku dziesiątki pól, tablic) → wskaźnik odbiorcy, aby uniknąć kosztownych kopii.
- 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).
- 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:
type Celsius float64
func (c Celsius) ToFahrenheit() float64 {
return float64(c)*9/5 + 32
}
t := Celsius(20)
fmt.Println(t.ToFahrenheit()) // 68Nie możesz ich definiować na typach z innych pakietów (np. int, string): musisz
najpierw utwórz lokalny nazwany typ.
Spróbuj
Dodaj metodę łańcuchową Greet() (odbiorca wartości) do osoby, która zwraca „ciao <Nazwa>”.
Pokaż wskazówkę
Odbiorca w nawiasie PRZED nazwą metody.
Rozwiązanie dostępne po 3 próbach
Dodaj metodę Rename(n string) z odbiornikiem POINTER, który zmienia p.Name.
Pokaż wskazówkę
Odbiornik wskaźnika: `(p *Person)`.
Rozwiązanie dostępne po 3 próbach
Kiedy należy używać odbiornika wskaźnika (p *T)?
func (t ?T) M() { ... }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↔*Tw 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).